Javascript 如何导出一个过程';基于其实现的HM类型?

Javascript 如何导出一个过程';基于其实现的HM类型?,javascript,functional-programming,hindley-milner,Javascript,Functional Programming,Hindley Milner,给定这两个过程(用JavaScript编写) 我的问题是如何在不引用comp实现的情况下导出comp2 如果我们知道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)

给定这两个过程(用JavaScript编写)

我的问题是如何在不引用
comp
实现的情况下导出
comp2


如果我们知道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))
但是,如果我们只知道
comp
的类型,而不知道它的实现呢?我是否可以对
comp
的类型执行某种替换/评估,以
comp2
的类型结束,而不是评估代码来确定类型


考虑到这一点,问题变得更难了……(至少对我来说)

如果我们想使用
map
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

如何认识辛德利·米尔纳让我们看看我们知道些什么。让我们单独来看一下comp2的实现:

comp2 = comp comp comp

让我们考虑<代码> COMP> /Cord>的类型签名:

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

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

comp2 = (comp (comp :: b -> c)
              (comp :: a -> b))
我们可以首先尝试将
b->c
comp
类型统一起来,以确定
b
c
是什么,但我们需要进行一些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

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

精彩的解释。多谢各位^_^
b          -> c
(b1 -> c1) -> (a1 -> b1) -> (a1 -> c1)

b = b1 -> c1
c = (a1 -> b1) -> (a1 -> c1)
a          -> b
(b2 -> c2) -> (a2 -> b2) -> (a2 -> c2)

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

b1 = a2 -> b2
c1 = a2 -> c2
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