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)
的类型签名由于其参数而受到约束。即使