Haskell:手动计算lambda表达式-确定常规类型
首先,如果我没有在正确的网站上发布这篇文章,我很抱歉,因为我不确定这是不是一个数学问题,而不是一个编程问题,但是因为我在Haskell中使用这篇文章,并将结果与Haskell解释器进行比较,我想我应该在这里问一下 所以我基本上是在Haskell中计算lambda表达式,我是手动操作的(为了准备考试,因为我将被迫在纸上操作)。我得到了一些表达式,我必须在计算后写下它们的一般类型。为此,我使用了一个解释器来获得一个稍微正确的答案 特别是,我将计算以下表达式:Haskell:手动计算lambda表达式-确定常规类型,haskell,lambda,evaluation,lambda-calculus,Haskell,Lambda,Evaluation,Lambda Calculus,首先,如果我没有在正确的网站上发布这篇文章,我很抱歉,因为我不确定这是不是一个数学问题,而不是一个编程问题,但是因为我在Haskell中使用这篇文章,并将结果与Haskell解释器进行比较,我想我应该在这里问一下 所以我基本上是在Haskell中计算lambda表达式,我是手动操作的(为了准备考试,因为我将被迫在纸上操作)。我得到了一些表达式,我必须在计算后写下它们的一般类型。为此,我使用了一个解释器来获得一个稍微正确的答案 特别是,我将计算以下表达式: t2 = ( \x y z ->
t2 = ( \x y z -> y.x.y );
tx2 = (\x y z -> y.z.z);
tp2 = (\x -> \y -> (x.y.x));
td2 = (\x y z f -> y(z(z(f))));
tm2 = (\z -> \y -> \x -> z.y.x);
由于我不是100%了解如何做到这一点,我设计了一种方法。首先,我创建了一个模板,该模板有点类似于计算表达式的外观。也就是说,如果左侧大小('lambda'd')变量的一部分位于右侧(因为在每种情况下它几乎都是函数组成),那么它就是一个函数。如果不是,它只是一个变量。之后,我尝试尽可能地拟合函数的一般类型,得到了一些半正确的结果,但我确信我仍然犯了一些错误。以下是我的整个评估过程:
t2 = ( \x y z -> y.x.y );
(_ -> _) -> (_ -> _) -> c -> _ -> _
y(x(y))
assume:
y :: a -> b
x :: b -> a
result: (b -> a) -> (a -> b) -> c -> a -> b
interpreter: (a -> b) -> (b -> a) -> c -> b -> a
z不在右侧,因此在本例中它不是一个函数。我给它赋值为c。现在我看右边的作文。我从右到左,将a->b分配给y,因为我不知道它的输入或输出。由于x使用y的结果作为输入,并且y再次使用x'输出作为输入,因此x是b->a。我可以直接插入到我的模板中。
正如你所看到的,这和我通过解释器得到的结果不完全一样,但它只是a和b被切换,所以它看起来并没有错
tx2 = (\x y z -> y.z.z);
a -> (_ -> _) -> (_ -> _) -> _ -> _
y(z(z))
assume:
z :: b -> b
y :: b -> c
result: a -> (b -> c) -> (b -> b) -> b -> c
interpreter: a -> (b -> c) -> (b -> b) -> b -> c
同上。因为z在函数合成中使用它自己,所以我假设它具有相同的输入和输出。y使用z的输出作为输入,并且有一些未知的输出,因此c。这似乎与我的翻译相符
tp2 = (\x -> \y -> (x.y.x));
(_ -> _) -> (_ -> _) -> _ -> _
x(y(x))
assume:
x :: a -> b
y :: b -> a
result: (a -> b) -> (b -> a) -> a -> b
interpreter: (a -> b) -> (b -> a) -> a -> b
与第一个示例基本相同,只是我没有未使用的lambda变量
td2 = (\x y z f -> y(z(z(f))));
a -> (_ -> _) -> (_ -> _) -> (_ -> _) -> _ -> _
y(z(z(f)))
assume:
f :: _ -> b
z :: b -> b
y :: b -> c
assume: a -> (b -> c) -> (b -> b) -> (_ -> b) -> b -> c
result: a -> (b -> c) -> (b -> b) -> (b -> b) -> b -> c
interpreter: a -> (b -> c) -> (b -> b) -> b -> c
在这种情况下,除了x以外的一切都是函数。f的输入最初我并不知道,我只是把它留为空。z在合成中使用f的输出和它自己的输出,所以我只分配它b->b。y使用z的输出,并且本身有一个未知的输出,所以它得到b->c。
现在我将它插入到我的模板中,但仍然缺少f的输入。因为f后面有一个b,所以我假设它也使用b作为输入。
现在有了第一个真正的问题:在解释器给出的答案中,f消失在哪里了?我只能假设,因为它使用与z相同的输入/输出,并且基本上是由z组成的,所以它只是简化为一个b->b,但我不确定这一点
tm2 = (\z -> \y -> \x -> z.y.x);
tm2 = (\z y x -> z.y.x);
(_ -> _) -> (_ -> _) -> (_ -> _) -> _ -> _
z(y(x))
assume:
x = a -> b
y = b -> _
z = _ -> _
assume: (_ -> _) -> (b -> _) -> (a -> b) -> _ -> _
result?: (a -> c) -> (b -> a) -> (a -> b) -> a -> c
interpreter: (a -> b) -> (c -> a) -> (d -> c) -> d -> b
这里是所有函数的分界点:z、y和x是函数。我只是把a->b赋值给x,这意味着y的输入是b。此时我不知道输出结果。z也是一样,因为我不知道y的输出。
因此,在我将它们输入模板后,唯一真正留给我的就是填补未知值的空白。因为x需要a作为输入,这意味着它后面有一个a。这意味着它也是z的输入。因为它是z的输入,所以我可以假设它是y的输出。唯一需要填写的是z的输出,我只给它赋值一个c,因为我不知道它可能是什么
正如你所看到的,这根本不是翻译给我的。虽然左手边可能还是有些相似,但我根本不明白右手边会发生什么。为什么是d->b?它不应该是(z(y(x))的结果吗?它应该有z的输入/输出
提前感谢您为我提供的任何帮助。您可以利用以下三个基本属性:
\x y->z
相当于\x->\y->z
x(y)
,您都知道x
必须是一个函数,并且它的第一个参数与表达式y
的类型匹配
的类型,即(b->c)->(a->b)->a->c
。此外,
是右关联的(即a.b.c
与a.(b.c)
相同)t2 = ( \x y z -> y.x.y );
tm2 = (\z -> \y -> \x -> z.y.x);
显然,它是三个参数的函数,所以它的类型类似于
t2 :: ty0 -> ty1 -> ty2 -> ty3
我在这里使用ty0
到ty3
来表示要推断的类型ty0
是x
参数的类型,ty1
是y
,ty2
是z
的类型,ty3
是结果值的类型(即表达式y.x.y
)
我首先要确定类型ty3
(由表达式y.x.y
定义),因为这样做的同时,您还可以找到所用参数的类型。未使用的参数可以有任何类型:
y.x.y
相当于y.(x.y)
(由于
的右关联性,请参见上文第3项)。因此,您可以从考虑子表达式x.y
开始x::(b->c)
和y:(a->b)
,因此x.y::a->c
(由于
的类型,再次参见上文第3条)y::(a->b)
和x.y::a->c
。记住这一点,y.(x.y)
可以进行类型检查(即匹配
的类型)的唯一方法是当c~a
时,即c
和a
是同一类型x::b->a
(我们的ty0
!)和y::a->b
(
ty1)和
y.(x.y)::a->b(我们称之为ty3`ab
tm2 = (\z -> \y -> \x -> z.y.x);
tm2 :: (c -> d) -> (b -> c) -> (a -> b) -> (a -> d)