我正在尝试在Haskell中实现不带Prelude的布尔值。
计算表达式beq true true "TRUE" "FALSE"
时可以。但是,当我尝试评估beq' true true "TRUE" "FALSE"
时,由于预期类型和实际类型之间的某些差异而导致失败。
这是代码。
import qualified Prelude as P
i = \x -> x
k = \x y -> x
ki = k i
true = k
false = ki
not = \p -> p false true
beq = \p q -> p (q true false) (q false true)
beq' = \p q -> p q (not q)
所以我检查了这些推论的类型。
*Main> :type beq
beq
:: (t1 -> t1 -> t2)
-> ((p1 -> p1 -> p1) -> (p2 -> p2 -> p2) -> t1) -> t2
*Main> :type beq'
beq'
:: (((p1 -> p2 -> p2) -> (p3 -> p4 -> p3) -> t1) -> t1 -> t2)
-> ((p1 -> p2 -> p2) -> (p3 -> p4 -> p3) -> t1) -> t2
那不相等。
这是问题。
我认为它具有相同的类型签名,因为beq
和beq'
在折叠和替换时似乎产生相同的结果。就像有很多方法可以实现一个功能。但事实并非如此。 Haskell中有一些秘密规则和语法吗?
如果我想使用功能beq
来编写not
,我该如何使其工作?
答案 0 :(得分:8)
教会编码在无类型演算中非常有效。
添加类型时,事情变得更加复杂。例如,对于简单类型,编码会丢失。如果支持更高的等级,则可以通过多态来恢复它们。请注意,类型推断不适用于更高类型,因此需要一些显式类型标注。
例如,您的not
应写为:
{-# LANGUAGE RankNTypes #-}
type ChBool = forall a. a -> a -> a
not :: ChBool -> ChBool
not f x y = f y x
将布尔值建模为多态函数很重要,因为否则它们只能用于单个类型,从而使许多示例失败。例如,考虑
foo :: Bool -> (Int, String)
foo b = (b 3 2, b "aa" "bb")
b
在这里需要使用两次,一次在Int
上,一次在String
上。如果Bool
不是多态的,它将失败。
此外,您似乎确信可以在还原前后将Beta减少Haskell表达式和推断的类型相同。通常,这是不对的,正如您在实验中发现的那样。要了解原因,这是一个简单的示例:
id1 x = x
显然,这里的推断类型为id1 :: forall a. a -> a
。相反,请考虑以下变体:
id2 x = (\ _ -> x) e
请注意,id2
会降低到id1
,无论e
可能是什么。但是,通过仔细选择e
,我们可以限制x
的类型。例如。让我们选择e = x "hello"
id2 x = (\ _ -> x) (x "hello")
现在,由于id2 :: forall b. (String -> b) -> String -> b
只能是x
接受函数,因此推断的类型为String
。 e
不会被求值并不重要,类型推断算法无论如何都会使类型正确。这使得id2
的推断类型不同于id1
的推断类型。