Haskell 组合子的类型签名与其等价Lambda函数的类型签名不匹配
考虑这个组合词:Haskell 组合子的类型签名与其等价Lambda函数的类型签名不匹配,haskell,lambda-calculus,combinators,type-signature,combinatory-logic,Haskell,Lambda Calculus,Combinators,Type Signature,Combinatory Logic,考虑这个组合词: S (S K) 将其应用于参数X和Y: S (S K) X Y 其合同为: X Y 我将S(sk)转换为相应的Lambda项,并得到以下结果: (\x y -> x y) 我使用Haskell WinGHCi工具获取(\x y->x y)的类型签名,它返回: (t1 -> t) -> t1 -> t ((t -> t1) -> t) -> (t -> t1) -> t 这对我来说是有道理的 接下来,我使用WinG
S (S K)
将其应用于参数X和Y:
S (S K) X Y
其合同为:
X Y
我将S(sk)转换为相应的Lambda项,并得到以下结果:
(\x y -> x y)
我使用Haskell WinGHCi工具获取(\x y->x y)的类型签名,它返回:
(t1 -> t) -> t1 -> t
((t -> t1) -> t) -> (t -> t1) -> t
这对我来说是有道理的
接下来,我使用WinGHCi获取s(sk)的类型签名,它返回:
(t1 -> t) -> t1 -> t
((t -> t1) -> t) -> (t -> t1) -> t
这对我来说没有意义。为什么类型签名不同
注:我将s、k和I定义为:
s = (\f g x -> f x (g x))
k = (\a x -> a)
i = (\f -> f)
我们从
k
和s
k :: t1 -> t2 -> t1
k = (\a x -> a)
s :: (t3 -> t4 -> t5) -> (t3 -> t4) -> t3 -> t5
s = (\f g x -> f x (g x))
因此,通过将k
作为s
的第一个参数,我们将k
的类型与s
的第一个参数的类型统一起来,并在类型处使用s
s :: (t1 -> t2 -> t1) -> (t1 -> t2) -> t1 -> t1
因此我们获得
s k :: (t1 -> t2) -> t1 -> t1
s k = (\g x -> k x (g x)) = (\g x -> x)
然后在s(sk)
中,外部s
用于类型(t3=t1->t2
,t4=t5=t1
)
将其应用于sk
将删除第一个参数的类型,并留给我们
s (s k) :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
总结:在Haskell中,
s(sk)
的类型是从其组成子表达式的类型派生出来的,而不是从其对参数的影响派生出来的。因此,它的类型没有lambda表达式那么一般,lambda表达式表示s(sk)
所使用的类型系统基本上与简单类型的lambda演算相同(您没有使用任何递归函数或递归类型)。简单类型的lambda演算不是完全通用的;它不是图灵完备的,不能用于编写一般递归。SKI组合子演算是图灵完备的,可以用来写不动点组合子和一般递归;因此,SKI combinator演算的全部功能不能用简单类型的lambda演算来表示(尽管它可以用非类型的lambda演算来表示)。感谢所有回答我问题的人。我研究过你的回答。为了确保我理解我所学到的东西,我已经为我的问题写下了自己的答案。如果我的回答不正确,请告诉我
我们从
k
和s
的类型开始:
k :: t1 -> t2 -> t1
k = (\a x -> a)
s :: (t3 -> t4 -> t5) -> (t3 -> t4) -> t3 -> t5
s = (\f g x -> f x (g x))
让我们首先确定(sk)
的类型签名
回想s
的定义:
s = (\f g x -> f x (g x))
s = (\f g x -> f x (g x))
(\g x -> x)
用k
代替f
会导致(\f g x->f x(g x))
签约:
(\g x -> k x (g x))
(\g x -> (s k) x (g x))
重要g和x的类型必须与k
的类型签名一致
回想一下,k
具有以下类型的签名:
k :: t1 -> t2 -> t1
s k :: (t1 -> t2) -> t1 -> t1
因此,对于这个函数定义kx(gx)
,我们可以推断:
的类型是x
的第一个参数的类型,即类型k
t1
的第二个参数的类型是k
,因此t2
的结果必须是(gx)
t2
将g
作为其参数,我们已经确定其类型为x
。所以t1
的类型签名是(gx)
(t1->t2)
的结果类型是k
,因此t1
的结果是(sk)
t1
(\gx->kx(gx))
的类型签名如下:
(t1 -> t2) -> t1 -> t1
((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
接下来,k
被定义为始终返回其第一个参数。因此:
k x (g x)
与此相关的合同:
x
(\g x -> x)
(g x)
(\g x -> g x)
这是:
(\g x -> k x (g x))
(\g x -> (s k) x (g x))
与此相关的合同:
x
(\g x -> x)
(g x)
(\g x -> g x)
好的,现在我们已经计算出(sk)
:
现在让我们确定s(sk)
的类型签名
我们以同样的方式进行
回想s
的定义:
s = (\f g x -> f x (g x))
s = (\f g x -> f x (g x))
(\g x -> x)
将(sk)
替换为f
会导致(\f g x->f x(g x))
与:
(\g x -> k x (g x))
(\g x -> (s k) x (g x))
重要g和x
的类型必须与(sk)
的类型签名一致
回想一下,(sk)
具有以下类型的签名:
k :: t1 -> t2 -> t1
s k :: (t1 -> t2) -> t1 -> t1
因此,对于这个函数定义(sk)x(gx)
,我们可以推断:
的类型是x
的第一个参数的类型,它是(sk)
的类型(t1->t2)
的第二个参数的类型是(sk)
,因此t1
的结果必须是(gx)
t1
的参数是g
,我们已经确定它的类型是x
。 所以(t1->t2)
的类型签名是(gx)
((t1->t2)->t1)
的结果类型是(sk)
,因此t1
的结果是sk
t1
(\gx->(sk)x(gx))
的类型签名如下:
(t1 -> t2) -> t1 -> t1
((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
早些时候,我们确定sk
具有以下定义:
s = (\f g x -> f x (g x))
s = (\f g x -> f x (g x))
(\g x -> x)
也就是说,它是一个接受两个参数并返回第二个参数的函数
因此,这:
(s k) x (g x)
与此相关的合同:
x
(\g x -> x)
(g x)
(\g x -> g x)
这是:
(\g x -> k x (g x))
(\g x -> (s k) x (g x))
与此相关的合同:
x
(\g x -> x)
(g x)
(\g x -> g x)
好的,现在我们已经算出了s(sk)
最后,将s(sk)
的类型签名与此函数的类型签名进行比较:
p = (\g x -> g x)
p
的类型为:
p :: (t1 -> t) -> t1 -> t
p
和s(sk)
具有相同的定义(\gx->gx)
,为什么它们具有不同的类型签名
s(sk)
与p
具有不同类型的签名的原因是对p
没有限制。我们看到(sk)
中的s
被约束为与k
的类型签名一致,并且s(sk)
中的第一个s
被约束为与(sk)
的类型签名一致。因此,s(sk)
的类型签名由于其参数而受到约束。即使