代码之家  ›  专栏  ›  技术社区  ›  Mulan

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

  •  2
  • Mulan  · 技术社区  · 8 年前

    鉴于这两个过程(用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 Type 没有 引用 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))

    叮当一声。扩展的评估匹配 组件2 的类型。没有人对此印象深刻。

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

    但如果我们只知道 comp公司 类型 并且做到了 知道它 实施 ? 与其评估代码以确定类型,我是否可以对 comp公司 的类型结束 组件2 的类型?


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

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

    一定有办法,对吧?这不是什么 algebraic data types 都是关于吗?


    让我们看一个简单的例子来澄清我的问题:如果我们有一个类似 add map

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

    如果我们想使用 地图 添加 我们可以找出类型 系统地 不知道 添加 地图 的实现

    // add :: Number -> Number -> Number
    // map :: (a -> b) -> [a] -> [b]
    
    // add6 :: Number -> Number
    let add6 = add (6) 
    
    // mapAdd6 :: [Number] -> [Number]
    let mapAdd6 = map(add6)
    

    这真的很强大,因为它允许您在不必深入挖掘实现的情况下对没有编写的代码进行推理

    然而,当尝试使用 组件2

    // 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

    1 回复  |  直到 8 年前
        1
  •  3
  •   Alexis King    8 年前

    让我们看看我们知道什么。让我们看看 comp2 s独立实现:

    comp2 = comp comp comp
    

    让我们考虑一下 comp s类型签名:

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

    现在,结果是 将是 comp公司 应用于两个参数,即 comp公司 类型签名。因此,我们知道 组件2 类型为 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
    

    现在,回到我们原来的类型 组件2 ,我们可以执行一系列替换,最终得到一个完整的类型:

    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
    

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