簡體   English   中英

一些帶有教堂數字的操作的類型簽名聲明

[英]Type signature declaration of some operations with Church numerals

我試圖在Haskell中實現教堂數字。 這是我的代碼:

-- Church numerals in Haskell.
type Numeral a = (a -> a) -> (a -> a)

churchSucc :: Numeral a -> Numeral a
churchSucc n f = \x -> f (n f x)

-- Operations with Church numerals.
sum :: Numeral a -> Numeral a -> Numeral a
sum m n = m . churchSucc n

mult :: Numeral a -> Numeral a -> Numeral a
mult n m = n . m

-- Here comes the first problem
-- exp :: Numeral a -> Numeral a -> Numeral a
exp n m = m n

-- Convenience function to "numerify" a Church numeral.
add1 :: Integer -> Integer
add1 = (1 +)

numerify :: Numeral Integer -> Integer
numerify n = n add1 0

-- Here comes the second problem
toNumeral :: Integer -> Numeral Integer
toNumeral 0 = zero
toNumeral (x + 1) = churchSucc (toNumeral x)

我的問題來自求冪。 如果我聲明toNumeralexp的類型簽名,則代碼不會編譯。 但是,如果我注釋類型簽名聲明,則一切正常。 toNumeralexp的正確聲明是什么?

無法用您的方式寫exp的原因是它涉及將Numeral作為參數傳遞給Numeral 這需要有一個Numeral (a -> a) ,但是您只有一個Numeral a 你可以寫成

exp :: Numeral a -> Numeral (a -> a) -> Numeral a
exp n m = m n

除了不應該使用x + 1類的模式外,我看不到toNumeral什么問題。

toNumeral :: Integer -> Numeral a -- No need to restrict it to Integer
toNumeral 0 = \f v -> v
toNumeral x
  | x > 0 = churchSucc $ toNumeral $ x - 1
  | otherwise = error "negative argument"

另外,因為m . churchSucc n ,您的sum有誤m . churchSucc n m . churchSucc nm * (n + 1) ,因此應為:

sum :: Numeral a -> Numeral a -> Numeral a
sum m n f x = m f $ n f x -- Repeat f, n times on x, and then m more times.

但是,教堂數字是適用於所有類型的函數。 也就是說, Numeral String不應與Numeral Integer不同,因為Numeral不關心它正在處理什么類型。 這是一種通用的量化方法 :對於所有類型a(a -> a) -> (a -> a) RankNTypes (a -> a) -> (a -> a) RankNTypes (a -> a) -> (a -> a)Numeral是一個函數,使用RankNTypes編寫為type Numeral = forall a. (a -> a) -> (a -> a) type Numeral = forall a. (a -> a) -> (a -> a)

這是有道理的:教堂數字由其函數參數重復多少次來定義。 \\fv -> v調用f 0次,因此它為0, \\fv -> fv為1,依此\\fv -> fv 。強制Numeral為所有a起作用,確保它只能這樣做。 但是,允許Numeral關心fv具有什么類型將消除該限制,並允許您編寫(\\fv -> "nope") :: Numeral String ,即使這顯然不是Numeral

我會這樣寫

{-# LANGUAGE RankNTypes #-}

type Numeral = forall a. (a -> a) -> (a -> a)

_0 :: Numeral
_0 _ x = x
-- The numerals can be defined inductively, with base case 0 and inductive step churchSucc
-- Therefore, it helps to have a _0 constant lying around

churchSucc :: Numeral -> Numeral
churchSucc n f x = f (n f x) -- Cleaner without lambdas everywhere

sum :: Numeral -> Numeral -> Numeral
sum m n f x = m f $ n f x

mult :: Numeral -> Numeral -> Numeral
mult n m = n . m

exp :: Numeral -> Numeral -> Numeral
exp n m = m n

numerify :: Numeral -> Integer
numerify n = n (1 +) 0

toNumeral :: Integer -> Numeral
toNumeral 0 = _0
toNumeral x
  | x > 0 = churchSucc $ toNumeral $ x - 1
  | otherwise = error "negative argument"

相反,它看上去更干凈,並且比原始版本更不容易遇到障礙。

演示:

main = do out "5:" _5
          out "2:" _2
          out "0:" _0
          out "5^0:" $ exp _5 _0
          out "5 + 2:" $ sum _5 _2
          out "5 * 2:" $ mult _5 _2
          out "5^2:" $ exp _5 _2
          out "2^5:" $ exp _2 _5
          out "(0^2)^5:" $ exp (exp _0 _2) _5
       where _2 = toNumeral 2
             _5 = toNumeral 5
             out :: String -> Numeral -> IO () -- Needed to coax the inferencer
             out str n = putStrLn $ str ++ "\t" ++ (show $ numerify n)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM