鉴于这两个程序(用 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
的Hindley-Milner 类型 comp
如果我们知道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)
总有办法吧?这不就是代数数据类型的全部意义吗?
让我们看一个简化的例子来澄清我的问题:如果我们有一个类似add
and的函数map
......
// add :: Number -> Number -> Number
// map :: (a -> b) -> [a] -> [b]
如果我们想定义一个函数 usingmap
并且我们可以在不知道or的实现的情况下系统地add
找出类型 add
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
例子来做这件事时,我很快就被卡住了
// comp :: (b -> c) -> (a -> b) -> (a -> c)
// comp2 :: ??
const comp2 = comp (comp) (comp)
// initial type
(b -> c) -> (a -> b) -> (a -> c)
// apply to comp once ... ???
[(b -> c) -> (a -> b) -> (a -> c)] -> (a -> b) -> (a -> c)
// apply the second time ... ???
[(b -> c) -> (a -> b) -> (a -> c)] -> [(b -> c) -> (a -> b) -> (a -> c)] -> (a -> c)
// no... this can't be right
HINDLEY MILNER 如何