Functional programming ML中的派生类型表达式

Functional programming ML中的派生类型表达式,functional-programming,types,sml,ml,Functional Programming,Types,Sml,Ml,全部, 我想在ML中为下面的函数派生类型表达式: fun f x y z = y (x z) 现在我知道键入相同的类型将生成类型表达式。但我想手工推导这些值 另外,请提及导出类型表达式时要遵循的一般步骤。要确定某个类型,您需要查看使用它的每个位置。例如,如果您看到val h=hd l,您知道l是一个列表(因为hd将列表作为参数),并且您还知道h的类型是l是一个列表的类型。因此,假设h的类型是a,而l的类型是a列表(其中a是占位符)。现在如果你看到val h2=h*2,你知道h和h2是ints,

全部,

我想在ML中为下面的函数派生类型表达式:

fun f x y z = y (x z)
现在我知道键入相同的类型将生成类型表达式。但我想手工推导这些值


另外,请提及导出类型表达式时要遵循的一般步骤。

要确定某个类型,您需要查看使用它的每个位置。例如,如果您看到
val h=hd l
,您知道
l
是一个列表(因为
hd
将列表作为参数),并且您还知道
h
的类型是
l
是一个列表的类型。因此,假设
h
的类型是
a
,而
l
的类型是
a列表(其中
a
是占位符)。现在如果你看到
val h2=h*2
,你知道
h
h2
int
s,因为
2
是一个int,您可以将一个int与另一个int相乘,两个int相乘的结果就是一个int。因为我们之前说过
h
的类型是
a
,这意味着
a
int
,所以
l
的类型是
int list

让我们来讨论一下你的功能:

y (x z)

让我们按照表达式的顺序来考虑表达式:首先,你做代码> x z < /代码>,即你应用<代码> x <代码> > <代码> z < /代码>。这意味着

x
是一个函数,因此它的类型为
a->b
。由于
z
是作为函数的参数提供的,因此它必须具有
a
类型。因此,
xz
的类型是
b
,因为这是
x
的结果类型

现在调用
y
,结果为
xz
。这意味着
y
也是一个函数,其参数类型是
x
的结果类型,即
b
。因此
y
的类型为
b->c
。表达式
y(xz)
的类型也是因此
c
,因为这是
y
的结果类型

由于这些都是函数中的表达式,我们不能进一步限制类型,因此
x
y
z
的最一般类型分别是
'a->'b
'b->'c
'a
,整个表达式的类型是
'c

这意味着
f
的总体类型是
('a->'b)->('b->'c)->'a->'c


有关如何通过编程方式推断类型的解释,请阅读。

解释类型推断的另一种方法是为每个(子)表达式和每个(子)模式分配一个类型变量

然后,程序中的每个构造都有一个与该构造相关的类型变量相关的等式

例如,如果程序包含f x “a1是f的类型变量,“a2”是x的类型变量,“a3”是“fx”的类型变量

然后,应用程序将生成类型方程: 'a1='a2->'a3


然后,类型推断基本上涉及到求解声明的类型方程组。对于ML来说,这是通过使用统一来完成的,手工操作非常简单。

我将尝试以最机械的方式来完成,就像大多数编译器中的实现一样

让我们把它分解一下:

fun f x y z = y (x z)
这基本上是糖:

val f = fn x => fn y => fn z => y (x z)
让我们添加一些元语法类型变量(这些不是真正的SML类型,为了本例的缘故,它们只是占位符):

好的,我们可以从这里开始生成一个约束系统。T5是f的最终返回类型。目前,我们将只调用整个函数的最终类型“TX”-一些新的未知类型变量

因此,在您给出的示例中,将要生成的约束是函数应用程序。它告诉我们表达式中事物的类型。事实上,这是我们仅有的信息

那么应用程序告诉我们什么呢

忽略上面指定的类型变量,让我们看看函数体:

y (x z)
z不适用于任何对象,所以我们只需查找之前分配给它的类型变量(T4),并将其用作其类型

x应用于z,但我们还不知道它的返回类型,因此让我们为它生成一个新的类型变量,并使用前面指定的x(T2)类型来创建约束:

T2 = T4 -> T7
T5 = T8
TX = T2 -> T3 -> T4 -> T5
y应用于(x z)的结果,我们称之为T7。再一次,我们还不知道y的返回类型,所以我们只给它一个新的变量:

T3 = T7 -> T8
我们还知道y的返回类型是整个函数体的返回类型,我们前面称为“T5”,因此我们添加了约束:

T2 = T4 -> T7
T5 = T8
TX = T2 -> T3 -> T4 -> T5
对于紧凑性,我将对此略作模糊,并根据函数返回函数的事实为TX添加一个约束。这是可以用完全相同的方法推导出来的,只是有点复杂。如果您不相信我们最终会遇到这样的限制,希望您可以自己做这个练习:

T2 = T4 -> T7
T5 = T8
TX = T2 -> T3 -> T4 -> T5
现在,我们收集所有约束:

val f : TX = fn (x : T2) => fn (y : T3) => fn (z : T4) => y (x z) : T5
TX = T2 -> T3 -> T4 -> T5
T2 = T4 -> T7
T3 = T7 -> T8
T5 = T8
我们开始求解这个方程组,在约束系统和原始表达式中,用右手边代替左手边,从最后一个约束开始,一直到顶部

val f : TX = fn (x : T2) => fn (y : T3) => fn (z : T4) => y (x z) : T8
TX = T2 -> T3 -> T4 -> T8
T2 = T4 -> T7
T3 = T7 -> T8

val f : TX = fn (x : T2) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8
TX = T2 -> (T7 -> T8) -> T4 -> T8
T2 = T4 -> T7

val f : TX = fn (x : T4 -> T7) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8
TX = (T4 -> T7) -> (T7 -> T8) -> T4 -> T8

val f : (T4 -> T7) -> (T7 -> T8) -> T4 -> T8 = fn (x : T4 -> T7) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8
好吧,现在看起来很可怕。我们现在并不真正需要整个表达的主体——它只是为了在解释中提供一些清晰。基本上,在符号表中,我们会有如下内容:

val f : (T4 -> T7) -> (T7 -> T8) -> T4 -> T8
最后一步是将剩下的所有类型变量归纳为我们所知道和喜爱的更熟悉的多态类型。基本上,这只是一个过程,将第一个未绑定类型变量替换为“a,第二个”