137

我遇到了这个术语Hindley-Milner,我不确定是否理解它的含义。

我已阅读以下帖子:

但是在维基百科中没有这个术语的单一条目,通常会为我提供简明的解释。
注意-现在已添加一个

它是什么?
什么语言和工具实现或使用它?
你能提供一个简洁的答案吗?

4

3 回答 3

178

Hindley-Milner 是由 Roger Hindley(研究逻辑)和后来的 Robin Milner(研究编程语言)独立发现的类型系统。Hindley-Milner 的优点是

  • 支持多态函数;例如,一个函数可以为您提供与元素类型无关的列表长度,或者一个函数执行二叉树查找,而与存储在树中的键的类型无关。

  • 有时一个函数或值可以有多个类型,例如长度函数的例子:它可以是“整数到整数的列表”、“字符串到整数的列表”、“对整数的列表”等等在。在这种情况下,Hindley-Milner 系统的一个信号优势是每个类型良好的术语都有一个唯一的“最佳”类型,称为主要类型。list-length 函数的主要类型是“for any a, function from list of ato integer”。这a是一个所谓的“类型参数”,它在 lambda 演算中是显式的,在大多数编程语言中是隐式的参数多态性。(如果您在 ML 中编写长度函数的定义,您可以看到类型参数:

     fun 'a length []      = 0
       | 'a length (x::xs) = 1 + length xs
    
  • 如果一个术语具有 Hindley-Milner 类型,则可以推断出主体类型,而无需程序员进行任何类型声明或其他注释。(这是喜忧参半,因为任何人都可以证明谁曾经处理过大量没有注释的 ML 代码。)

Hindley-Milner 是几乎所有静态类型函数式语言的类型系统的基础。此类常用语言包括

所有这些语言都扩展了 Hindley-Milner;Haskell、Clean 和 Objective Caml 以雄心勃勃和不同寻常的方式做到了这一点。(处理可变变量需要扩展,因为基本的 Hindley-Milner 可以被颠覆,例如,使用包含未指定类型值列表的可变单元格。此类问题由称为值限制的扩展处理。)

许多其他基于类型函数式语言的次要语言和工具使用 Hindley-Milner。

Hindley-Milner 是System F的一个限制,它允许更多类型但需要程序员注释

于 2008-12-30T02:34:42.780 回答
12

您可以使用 Google Scholar 或 CiteSeer 或您当地的大学图书馆找到原始论文。第一个太老了,你可能得找杂志的装订本,我在网上找不到。我为另一个找到的链接已损坏,但可能还有其他链接。你肯定能找到引用这些的论文。

Hindley, Roger J,组合逻辑中对象的主要类型方案,美国数学会汇刊,1969 年。

Milner, Robin,类型多态性理论,计算机与系统科学杂志,1978 年。

于 2008-12-30T02:12:26.223 回答
6

C# 中的简单 Hindley-Milner 类型推断实现:

Hindley-Milner 对(Lisp-ish)S 表达式的类型推断,使用不到 650 行 C#

请注意,该实现仅在 270 行左右的 C# 行范围内(无论如何,对于算法 W 本身和支持它的少数数据结构)。

用法摘录:

    // ...

    var syntax =
        new SExpressionSyntax().
        Include
        (
            // Not-quite-Lisp-indeed; just tolen from our host, C#, as-is
            SExpressionSyntax.Token("\\/\\/.*", SExpressionSyntax.Commenting),
            SExpressionSyntax.Token("false", (token, match) => false),
            SExpressionSyntax.Token("true", (token, match) => true),
            SExpressionSyntax.Token("null", (token, match) => null),

            // Integers (unsigned)
            SExpressionSyntax.Token("[0-9]+", (token, match) => int.Parse(match)),

            // String literals
            SExpressionSyntax.Token("\\\"(\\\\\\n|\\\\t|\\\\n|\\\\r|\\\\\\\"|[^\\\"])*\\\"", (token, match) => match.Substring(1, match.Length - 2)),

            // For identifiers...
            SExpressionSyntax.Token("[\\$_A-Za-z][\\$_0-9A-Za-z\\-]*", SExpressionSyntax.NewSymbol),

            // ... and such
            SExpressionSyntax.Token("[\\!\\&\\|\\<\\=\\>\\+\\-\\*\\/\\%\\:]+", SExpressionSyntax.NewSymbol)
        );

    var system = TypeSystem.Default;
    var env = new Dictionary<string, IType>();

    // Classic
    var @bool = system.NewType(typeof(bool).Name);
    var @int = system.NewType(typeof(int).Name);
    var @string = system.NewType(typeof(string).Name);

    // Generic list of some `item' type : List<item>
    var ItemType = system.NewGeneric();
    var ListType = system.NewType("List", new[] { ItemType });

    // Populate the top level typing environment (aka, the language's "builtins")
    env[@bool.Id] = @bool;
    env[@int.Id] = @int;
    env[@string.Id] = @string;
    env[ListType.Id] = env["nil"] = ListType;

    //...

    Action<object> analyze =
        (ast) =>
        {
            var nodes = (Node[])visitSExpr(ast);
            foreach (var node in nodes)
            {
                try
                {
                    Console.WriteLine();
                    Console.WriteLine("{0} : {1}", node.Id, system.Infer(env, node));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            Console.WriteLine();
            Console.WriteLine("... Done.");
        };

    // Parse some S-expr (in string representation)
    var source =
        syntax.
        Parse
        (@"
            (
                let
                (
                    // Type inference ""playground""

                    // Classic..                        
                    ( id ( ( x ) => x ) ) // identity

                    ( o ( ( f g ) => ( ( x ) => ( f ( g x ) ) ) ) ) // composition

                    ( factorial ( ( n ) => ( if ( > n 0 ) ( * n ( factorial ( - n 1 ) ) ) 1 ) ) )

                    // More interesting..
                    ( fmap (
                        ( f l ) =>
                        ( if ( empty l )
                            ( : ( f ( head l ) ) ( fmap f ( tail l ) ) )
                            nil
                        )
                    ) )

                    // your own...
                )
                ( )
            )
        ");

    // Visit the parsed S-expr, turn it into a more friendly AST for H-M
    // (see Node, et al, above) and infer some types from the latter
    analyze(source);

    // ...

...产生:

id : Function<`u, `u>

o : Function<Function<`z, `aa>, Function<`y, `z>, Function<`y, `aa>>

factorial : Function<Int32, Int32>

fmap : Function<Function<`au, `ax>, List<`au>, List<`ax>>

... Done.

另请参阅Brian McKenna 在 bitbucket 上的 JavaScript 实现,这也有助于入门(对我有用)。

'HTH,

于 2012-07-04T04:53:12.373 回答