如何确定q2和q3的类型?当我输入时,模块给了我类型,但是手动执行此操作的可靠方法是什么?请帮忙。谢谢。
q2 x y z w = w (x y) (z y)
Type is: q2 :: (a -> b) -> a -> (a -> c) -> (b -> c -> d) -> d
q3 f g x y = (f.g) x == y
Type is: q3 :: Eq a => (b -> a) -> (c -> b) -> c -> a -> Bool
答案 0 :(得分:11)
类型信息从现有结构流向新结构。让我们来看看你的例子:
q2 x y z w = w (x y) (z y)
这可能不是很明显,但是这个函数已经调用了一些类型化的Haskell原语。特别是,它使用具有类型
的函数应用程序($) :: (a -> b) -> a -> b
实际上我们可以使用($)
语法来使我们更加明确地使用函数应用程序。
q2 x y z w = (w $ x $ y) $ z $ y
或者,我们可以用Javascript-esque语法重新构造它,以便更清楚地看到应用程序
q2(x)(y)(z)(w) = w(x(y))(z(y))
无论如何,应该清楚有4个功能应用程序正在发生。从这些我们将产生的信息为我们提供q2
的主要类型。
扩展类型推断的主要方法是“统一”,也就是说,如果我们知道单个事物具有类型A
和B
,那么我们必须能够转换A
和B
进入C
,第三种类型同意A
和B
。它可能只是A
或B
偶数。
| A | B | C |
|--------|--------|--------|
| String | a | String |
| Int | String | <fail> |
| a | b | c | (where we suppose a == b == c)
| a -> b | c -> d | e -> f | (where we suppose a == c == e
| | | | and b == d == f)
正如你所看到的,统一的另外两个特征:(1)它可能会失败,如果和(2)它有时会导致我们假设类型变量之间的平等。
一般来说,这就是推理的进展:我们为我们不知道的所有事物分配一个新的类型变量,然后尝试统一所有的部分。一路上我们可能会失败(因此我们说类型检查已经失败)或者我们将收集一大堆相等的信息,它们告诉我们我们已经引入了许多冗余类型变量。然后我们通过消除所有冗余变量来总结信息,直到我们不再需要陈述我们的等式。
id :: a -> a
3 :: Num a => a
3 :: Num b => b -- make the variable fresh / not conflict with `a`
id 3 :: Num c => c (supposing a == b == c)
id 3 :: Num a => a (supposing nothing, we've forgotten about b and c)
因此,我们可以将此流程应用于q2
。算术上做起来有点啰嗦,但很容易手工完成。我们正在寻找值q2
的类型。我们知道q2
需要4个参数并返回一些内容,因此我们可以立即构建该类型
q2 :: a -> b -> c -> d -> e
我们通过将x
,z
和w
的类型与应用($)
的类型统一为a
和{{1必须与函数兼容
c
并且他们的输入参数必须具有与其参数值q2 :: (f -> g) -> b -> (h -> i) -> d -> e
y :: b
最后,我们可以检查q2 :: (b -> g) -> b -> (b -> i) -> d -> e
,看看它是一个函数,它接受w
类型的一个参数,并返回另一个函数,该函数接受x y
类型的参数并返回东西
z y
由q2 :: (b -> g) -> b -> (b -> i) -> (g -> (i -> j)) -> e
的右关联性我们通常写为
(->)
最后,我们知道q2 :: (b -> g) -> b -> (b -> i) -> (g -> i -> j) -> e
的返回类型是整个函数的返回类型
w
最重要的是,q2 :: (b -> g) -> b -> (b -> i) -> (g -> i -> j) -> j
是最终的,最常见的类型。
有关详情,请调查Hindley-Milner and Algorithm W。我已经松散地涵盖了大部分细节,但还有一些其他想法,所有这些都可以更仔细地检查。
答案 1 :(得分:5)
只是分析有意义的事情:
首先我们有
q2 x y z w = w (x y) (z y)
让我们分解一下,从仅仅看q2 x y z w
我们得到它需要4个参数,以及返回类型:
q2 :: a -> b -> c -> d -> e
现在我们看一下这些,我们有w (x y) (z y)
,让我们把它分成小块:
(x y)
:我们使用x
作为函数,y
作为所述函数的参数,因此x
的类型为x :: b -> f
。所以q2
现在看起来像这样:
q2 :: (b -> f) -> b -> c -> d -> e
(z y)
:具有相同的风格,所以我们可以说我们对x
说的一样,但我们不知道x
是否并且z
返回相同的类型,因此z
看起来像z :: b -> g
。使q2
看起来像这样:
q2 :: (b -> f) -> b -> (b -> g) -> d -> e
注意: (b -> f)
和(b -> g)
会返回不同的类型,因为没有迹象(至少到现在为止)他们返回相同的类型。
w (x y) (z y)
:此处我们使用w
作为函数,将(x y)
和(z y)
作为参数,因此现在w
的类型为w :: f -> g -> h
{1}}。制作q2
:
q2 :: (b -> f) -> b -> (b -> g) -> (f -> g -> h) -> e
注意: w
接受x
和z
的返回类型的参数。
q2 x y z w = w (x y) (z y)
:我们可以看到此函数执行的最后一件事是使用w
作为函数,因此w
返回的是q2
应返回的内容,所以最后q2
看起来像这样:
q2 :: (b -> f) -> b -> (b -> g) -> (f -> g -> h) -> h
希望它有所帮助,你应该自己q3
来练习。如果您遇到困难,请告诉我。
答案 2 :(得分:1)
一篇了解Haskell类型系统的好文章也是“在Haskell中输入Haskell”,在那里你可以真正看到一个程序如何完成Hindley-Milner类型的推断。 http://web.cecs.pdx.edu/~mpj/thih/
由于Haskell是强类型的,因此您可以始终推断所有类型的所有函数,从假设一个不受限制的类型变量开始,该变量可能被上下文限制为更具体的类型或类型类(需要其他函数的其他函数)特定类型)。
学家亚伯拉罕森很好地解释了这一点。 :)