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

Rog*_*llo 5 haskell combinators lambda-calculus combinatory-logic type-signature

考虑这个组合子:

S (S K)
Run Code Online (Sandbox Code Playgroud)

将其应用于参数XY:

S (S K) X Y
Run Code Online (Sandbox Code Playgroud)

它签订合同:

X Y
Run Code Online (Sandbox Code Playgroud)

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

(\x y -> x y)
Run Code Online (Sandbox Code Playgroud)

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

(t1 -> t) -> t1 -> t
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义.

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

((t -> t1) -> t) -> (t -> t1) -> t
Run Code Online (Sandbox Code Playgroud)

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

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

s = (\f g x -> f x (g x))
k = (\a x -> a)
i = (\f -> f)
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 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))
Run Code Online (Sandbox Code Playgroud)

因此,k作为第一个参数传递s,我们ks第一个参数的类型统一类型,并s在类型中使用

s :: (t1 -> t2 -> t1) -> (t1 -> t2) -> t1 -> t1
Run Code Online (Sandbox Code Playgroud)

因此我们得到了

s k :: (t1 -> t2) -> t1 -> t1
s k = (\g x -> k x (g x)) = (\g x -> x)
Run Code Online (Sandbox Code Playgroud)

然后s (s k),外部s用于类型(t3 = t1 -> t2,t4 = t5 = t1)

s :: ((t1 -> t2) -> t1 -> t1) -> ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
Run Code Online (Sandbox Code Playgroud)

应用它来s k删除第一个参数的类型并离开我们

s (s k) :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1
Run Code Online (Sandbox Code Playgroud)

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


new*_*cct 7

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


Rog*_*llo 2

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

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)) 
Run Code Online (Sandbox Code Playgroud)

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

回忆一下s的定义:

s = (\f g x -> f x (g x))
Run Code Online (Sandbox Code Playgroud)

k将承包f结果替换(\f g x -> f x (g x))为:

(\g x -> k x (g x))
Run Code Online (Sandbox Code Playgroud)

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

回想一下,它k具有以下类型签名:

   k :: t1 -> t2 -> t1
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • g我们已经确定其参数为xtype t1。所以 的类型签名(g x)(t1 -> t2).

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

所以, 的类型签名(\g x -> k x (g x))是这样的:

   (t1 -> t2) -> t1 -> t1
Run Code Online (Sandbox Code Playgroud)

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

k x (g x)
Run Code Online (Sandbox Code Playgroud)

合同如下:

x
Run Code Online (Sandbox Code Playgroud)

和这个:

(\g x -> k x (g x))
Run Code Online (Sandbox Code Playgroud)

合同如下:

(\g x -> x)
Run Code Online (Sandbox Code Playgroud)

好的,现在我们已经弄清楚了(s k)

   s k  :: (t1 -> t2) -> t1 -> t1 
   s k  =  (\g x -> x)
Run Code Online (Sandbox Code Playgroud)

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

我们以同样的方式进行。

回忆一下s的定义:

s = (\f g x -> f x (g x))
Run Code Online (Sandbox Code Playgroud)

(s k)将承包f结果替换(\f g x -> f x (g x))为:

(\g x -> (s k) x (g x))
Run Code Online (Sandbox Code Playgroud)

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

回想一下,它(s k)具有以下类型签名:

   s k :: (t1 -> t2) -> t1 -> t1
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • ghasx作为其参数,我们已经确定其类型为(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
Run Code Online (Sandbox Code Playgroud)

早些时候我们确定s k有这样的定义:

   (\g x -> x)
Run Code Online (Sandbox Code Playgroud)

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

因此,这个:

   (s k) x (g x)
Run Code Online (Sandbox Code Playgroud)

对此的合同:

   (g x)
Run Code Online (Sandbox Code Playgroud)

和这个:

   (\g x -> (s k) x (g x))
Run Code Online (Sandbox Code Playgroud)

合同如下:

   (\g x -> g x)
Run Code Online (Sandbox Code Playgroud)

好吧,现在我们已经弄清楚了s (s k)

   s (s k)  :: ((t1 -> t2) -> t1) -> (t1 -> t2) -> t1 
   s (s k)  =  (\g x -> g x)
Run Code Online (Sandbox Code Playgroud)

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

   p = (\g x -> g x)
Run Code Online (Sandbox Code Playgroud)

的类型p是:

   p :: (t1 -> t) -> t1 -> t
Run Code Online (Sandbox Code Playgroud)

p并且s (s k)具有相同的定义(\g x -> g x),那么为什么它们有不同的类型签名呢?

s (s k)具有不同类型签名的原因pp. 我们看到sin(s k)被约束为与 的类型签名一致k,并且第一个sins (s k)被约束为与 的类型签名一致(s k)。因此, 的类型签名s (s k)因其参数而受到限制。即使 和p具有相同的定义,但对和 的s (s k)约束是不同的。gx