我试图在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)
我的问题来自取幂。如果我声明toNumeral
和exp
的类型签名,则代码不会编译。但是,如果我评论类型签名声明,一切正常。 toNumeral
和exp
的正确声明是什么?
答案 0 :(得分:6)
无法按照您的方式编写exp
的原因是它涉及将Numeral
作为参数传递给Numeral
。这需要Numeral (a -> a)
,但您只有Numeral a
。你可以把它写成
exp :: Numeral a -> Numeral (a -> a) -> Numeral a
exp n m = m n
除了toNumeral
之类的模式不应该使用之外,x + 1
我没有看到什么问题。
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"
此外,您的sum
也有问题,因为m . churchSucc n
是m * (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
不应该关心它正在处理什么类型。这是通用量化:Numeral
是一个函数,适用于所有类型a
,(a -> a) -> (a -> a)
,使用RankNTypes
编写为{ {1}}。
这是有道理的:教会数字由其重复的函数参数的次数来定义。 type Numeral = forall a. (a -> a) -> (a -> a)
调用\f v -> v
0次,因此它为0,f
为1,等等。强制\f v -> f v
适用于所有Numeral
,确保它可以仅这样做。但是,允许a
关注哪种类型Numeral
和f
已删除限制,并允许您编写v
,即使这显然不是{{1} }}
我会把它写成
(\f v -> "nope") :: Numeral String
相反,它看起来更干净,并且不太可能遇到障碍而不是原始障碍。
演示:
Numeral