组合子的类型签名与其等效Lambda函数的类型签名不匹配

时间:2012-03-06 21:26:11

标签: haskell lambda-calculus combinators type-signature combinatory-logic

考虑这个组合子:

S (S K)

将其应用于参数X Y:

S (S K) X Y

它签约:

X Y

我将S(S K)转换为相应的Lambda术语并得到了这个结果:

(\x y -> x y)

我使用Haskell WinGHCi工具获取(\ x y - > x y)的类型签名并返回:

(t1 -> t) -> t1 -> t

这对我有意义。

接下来,我使用WinGHCi获取s(s k)的类型签名并返回:

((t -> t1) -> t) -> (t -> t1) -> t

这对我没有意义。为什么类型签名不同?

注意:我将s,k和i定义为:

s = (\f g x -> f x (g x))
k = (\a x -> a)
i = (\f -> f)

3 个答案:

答案 0 :(得分:9)

我们从ks

的类型开始
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的第一个参数统一起来,然后使用{{1在类型

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 (s k)用于类型(st3 = t1 -> t2

t4 = t5 = t1

将其应用于s :: ((t1 -> t2) -> t1 -> t1) -> ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1 会删除第一个参数的类型并留下

s k

总结:在Haskell中,s (s k) :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1 的类型来自其组成子表达式的类型,而不是它对其参数的影响。因此,它具有比表示s (s k)的效果的lambda表达式更少的通用类型。

答案 1 :(得分:7)

您使用的类型系统与简单类型的lambda演算基本相同(您不使用任何递归函数或递归类型)。简单类型的lambda演算并不完全通用;它不是图灵完备的,它不能用于编写一般递归。 SKI组合子演算是图灵完备的,可用于编写定点组合器和一般递归;因此,SKI组合子微积分的全部功效不能用简单类型的lambda演算表示(尽管它可以是无类型的lambda演算)。

答案 2 :(得分:2)

感谢所有回答我问题的人。我研究了你的回答。为了确保我理解我所学到的知识,我已经为自己的问题写了自己的答案。如果我的回答不正确,请告诉我。

我们从ks

的类型开始
   k  :: t1 -> t2 -> t1 
   k  =  (\a x -> a) 

   s  :: (t3 -> t4 -> t5) -> (t3 -> t4) -> t3 -> t5 
   s  =  (\f g x -> f x (g x)) 

让我们首先确定(s k)的类型签名。

回顾s的定义:

s = (\f g x -> f x (g x))

k替换为f会导致(\f g x -> f x (g x))签约:

(\g x -> k x (g x))

重要 g和x的类型必须与k的类型签名一致。

回想一下k有这种类型的签名:

   k :: t1 -> t2 -> t1

因此,对于此函数定义k x (g x),我们可以推断:

  • x的类型是k的第一个参数的类型,类型为t1

  • k的第二个参数的类型为t2,因此(g x)的结果必须为t2

  • g已将x作为其参数,我们已经确定其类型为t1。因此(g x)的类型签名为(t1 -> t2)

  • k的结果类型为t1,因此(s k)的结果为t1

因此,(\g x -> k x (g x))的类型签名就是:

   (t1 -> t2) -> t1 -> t1

接下来,k被定义为始终返回其第一个参数。所以这个:

k x (g x)

与此签订合同:

x

而且:

(\g x -> k x (g x))

与此签订合同:

(\g x -> x)

好的,现在我们已经找到了(s k)

   s k  :: (t1 -> t2) -> t1 -> t1 
   s k  =  (\g x -> x)

现在让我们确定s (s k)的类型签名。

我们以同样的方式进行。

回顾s的定义:

s = (\f g x -> f x (g x))

(s k)替换为f会导致(\f g x -> f x (g x))签约:

(\g x -> (s k) x (g x))

重要 gx的类型必须与(s k)的类型签名一致。

回想一下(s k)有这种类型的签名:

   s k :: (t1 -> t2) -> t1 -> t1

因此,对于此函数定义(s k) x (g x),我们可以推断:

  • x的类型是(s k)的第一个参数的类型,类型为(t1 -> t2)

  • (s k)的第二个参数的类型为t1,因此(g x)的结果必须为t1

  • gx作为参数,我们已经确定它的类型为(t1 -> t2)。 因此(g x)的类型签名为((t1 -> t2) -> t1)

  • (s k)的结果类型为t1,因此s (s k)的结果为t1

因此,(\g x -> (s k) x (g x))的类型签名就是:

   ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1

之前我们确定s k有这个定义:

   (\g x -> x)

也就是说,它是一个带有两个参数并返回第二个参数的函数。

因此,这个:

   (s k) x (g x)

与此相符:

   (g x)

而且:

   (\g x -> (s k) x (g x))

与此签订合同:

   (\g x -> g x)

好的,现在我们已经找到了s (s k)

   s (s k)  :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1 
   s (s k)  =  (\g x -> g x)

最后,将s (s k)的类型签名与此函数的类型签名进行比较:

   p = (\g x -> g x)

p的类型是:

   p :: (t1 -> t) -> t1 -> t

ps (s k)具有相同的定义(\g x -> g x),为什么他们有不同的类型签名?

s (s k)的类型签名与p不同的原因是p没有约束。我们发现s中的(s k)被限制为与k的类型签名一致,s中的第一个s (s k)被限制为一致类型签名为(s k)。因此,s (s k)的类型签名由于其参数而受到约束。即使ps (s k)具有相同的定义,gx上的约束也会有所不同。