如何根据其实现导出过程的HM类型?

时间:2016-07-31 09:53:39

标签: javascript functional-programming hindley-milner

鉴于这两个程序(用JavaScript编写)......

// comp :: (b -> c) -> (a -> b) -> (a -> c)
const comp = f=> g=> x=> f (g (x))

// comp2 :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
const comp2 = comp (comp) (comp)

我的问题是如何在没有引用comp2的实现的情况下派生comp的{​​{3}}

如果我们知道comp的实现,很容易......我们可以通过整个评估使用替换模型来得到扩展的表达式......

comp (comp) (comp)
= (f => g => x => f (g (x))) (comp) (comp) 
= x => comp (comp (x))
= y => comp (comp (y)) 
= y => (f => g => x => f (g (x))) (comp (y))
... keep going until ...
= f=> g=> x=> y=> f (g (x) (y))

环一丁。扩展的评估与comp2的类型匹配。没有人留下深刻的印象。

// comp2 :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
const comp2 = f=> g=> x=> y=> f (g (x) (y))

但是,如果我们只知道comp类型并且知道实现,该怎么办?我是否可以对comp的类型进行某种替换/评估,以最终获得comp2的类型,而不是评估代码以确定类型?

只有这样,问题变得更加困难......(至少对我而言)

// comp :: (b -> c) -> (a -> b) -> (a -> c)

// comp2 :: ???
const comp2 = comp (comp) (comp)

必须有办法,对吗?这不是Hindley-Milner Type的全部内容吗?

让我们看一个简化的例子来澄清我的问题:如果我们有像addmap这样的函数......

// add :: Number -> Number -> Number
// map :: (a -> b) -> [a] -> [b]

如果我们想使用mapadd定义一个函数,我们可以在不知道add或{{1>的情况下系统地找出类型 的类型实施

map

这非常强大,因为它允许您在不必深入了解实现的情况下推理您未编写的代码(同样多)

但是,当尝试使用// add :: Number -> Number -> Number // map :: (a -> b) -> [a] -> [b] // add6 :: Number -> Number let add6 = add (6) // mapAdd6 :: [Number] -> [Number] let mapAdd6 = map(add6) 示例时,我会很快被卡住

comp2

HINDLEY MILNER如何

1 个答案:

答案 0 :(得分:3)

让我们看看我们所知道的。让我们孤立地看一下comp2的实现:

comp2 = comp comp comp

让我们考虑comp的类型签名:

comp :: (b -> c) -> (a -> b) -> (a -> c)

现在,comp2的结果将是comp应用于两个参数的结果,这两个参数是comp类型签名的最右侧。因此,我们知道comp2的类型属于a -> c类型,我们只是不知道ac是什么。

但是,我们可以搞清楚。我们可以通过手动统一类型(通过知道两种类型需要相同)来完成这一工作,然后用已知类型变量替换具体类型。这两个参数都是comp,但它们应分别具有不同的类型:b -> ca -> b。让我们添加一些类型注释,使其更清晰:

comp2 = (comp (comp :: b -> c)
              (comp :: a -> b))

我们可以先尝试将b -> ccomp类型统一起来,以确定bc是什么,但我们需要进行一些alpha重命名这样我们的变量名就不会发生碰撞:

b          -> c
(b1 -> c1) -> (a1 -> b1) -> (a1 -> c1)

b = b1 -> c1
c = (a1 -> b1) -> (a1 -> c1)

接下来,我们可以对第二个参数做同样的事情,与a -> b类型统一:

a          -> b
(b2 -> c2) -> (a2 -> b2) -> (a2 -> c2)

a = b2 -> c2
b = (a2 -> b2) -> (a2 -> c2)

但是等等!我们现在对同一个类型变量b有两个不同的定义,所以这些定义也必须统一。让我们对这两种类型执行相同的过程:

b1         -> c1
(a2 -> b2) -> (a2 -> c2)

b1 = a2 -> b2
c1 = a2 -> c2

现在,回到我们为comp2提供的原始类型,我们可以执行一系列替换,最终得到一个完整的类型:

a -> c                                                 | type of comp2, from the return type of comp
(b2 -> c2) -> c                                        | substituting the definition of a
(b2 -> c2) -> (a1 -> b1) -> (a1 -> c1)                 | substituting the definition of c
(b2 -> c2) -> (a1 -> (a2 -> b2)) -> (a1 -> c1)         | substituting the definition of b1
(b2 -> c2) -> (a1 -> (a2 -> b2)) -> (a1 -> (a2 -> c2)) | substituting the definition of c1
(b2 -> c2) -> (a1 -> a2 -> b2) -> a1 -> a2 -> c2       | removing unnecessary parentheses
(c -> d) -> (a -> b -> c) -> a -> b -> d               | alpha renaming

您会注意到这与您手动指定的类型相同。