Hindley-Milner là gì?


124

Tôi đã gặp thuật ngữ Hindley-Milner này và tôi không chắc có nắm bắt được ý nghĩa của nó không.

Tôi đã đọc các bài viết sau:

Nhưng không có mục duy nhất cho thuật ngữ này trong wikipedia nơi thường cung cấp cho tôi một lời giải thích ngắn gọn.
Lưu ý - một cái đã được thêm vào

Nó là gì?
Những ngôn ngữ và công cụ thực hiện hoặc sử dụng nó?
Bạn có thể vui lòng cung cấp một câu trả lời súc tích?

Câu trả lời:


167

Hindley-Milner là một hệ thống loại được phát hiện độc lập bởi Roger Hindley (người đang xem xét logic) và sau đó là Robin Milner (người đang xem xét các ngôn ngữ lập trình). Ưu điểm của Hindley-Milner là

  • Nó hỗ trợ các chức năng đa hình ; ví dụ, một hàm có thể cung cấp cho bạn độ dài của danh sách độc lập với loại phần tử hoặc một hàm thực hiện tra cứu cây nhị phân độc lập với loại khóa được lưu trữ trong cây.

  • Đôi khi một hàm hoặc giá trị có thể có nhiều loại , như trong ví dụ về hàm độ dài: nó có thể là "danh sách các số nguyên thành số nguyên", "danh sách các chuỗi thành số nguyên", "danh sách các cặp thành số nguyên", và vì vậy trên. Trong trường hợp này, một lợi thế tín hiệu của hệ thống Hindley-Milner là mỗi thuật ngữ được gõ tốt có một loại "tốt nhất" duy nhất , được gọi là loại chính . Loại chính của hàm độ dài danh sách là "cho bất kỳ a, hàm nào từ danh sách ađến số nguyên". Dưới đây alà một "tham số loại", được trình bày rõ ràng trong phép tính lambda nhưng ẩn trong hầu hết các ngôn ngữ lập trình .đa hình tham số . (Nếu bạn viết định nghĩa của hàm độ dài trong ML, do đó bạn có thể thấy tham số loại:

     fun 'a length []      = 0
       | 'a length (x::xs) = 1 + length xs
    
  • Nếu một thuật ngữ có loại Hindley-Milner, thì loại chính có thể được suy ra mà không yêu cầu bất kỳ khai báo loại hoặc chú thích nào khác của lập trình viên. (Đây là một phước lành hỗn hợp, vì bất kỳ ai cũng có thể chứng thực người đã từng bị xử lý một đoạn lớn mã ML mà không có chú thích.)

Hindley-Milner là cơ sở cho hệ thống loại của hầu hết mọi ngôn ngữ chức năng được gõ tĩnh. Những ngôn ngữ được sử dụng phổ biến bao gồm

Tất cả các ngôn ngữ này đã mở rộng Hindley-Milner; Haskell, Clean và Objective Caml làm như vậy theo những cách đầy tham vọng và khác thường. (Các phần mở rộng được yêu cầu để xử lý các biến có thể thay đổi, vì Hindley-Milner cơ bản có thể bị phá hủy bằng cách sử dụng, ví dụ, một ô có thể thay đổi chứa danh sách các giá trị của loại không xác định. Các vấn đề như vậy được giải quyết bằng một phần mở rộng được gọi là hạn chế giá trị .)

Nhiều ngôn ngữ và công cụ nhỏ khác dựa trên các ngôn ngữ chức năng đánh máy sử dụng Hindley-Milner.

Hindley-Milner là một hạn chế của Hệ thống F , cho phép nhiều loại hơn nhưng đòi hỏi các chú thích của lập trình viên .


2
@NormanRamsey Tôi biết điều này là xấu xa nhưng cảm ơn vì đã làm sáng tỏ điều khiến tôi bực mình vô tận: Mỗi khi tôi nhắc đến hệ thống loại hindley-milner, có ai đó nói về suy luận kiểu đến mức tôi đã bắt đầu đặt câu hỏi về việc HM có phải là một loại hệ thống hoặc chỉ là thuật toán suy luận ... Thankyou là để tôi đoán wikipedia vì đã thông tin sai cho mọi người về vấn đề này đến mức họ thậm chí còn làm tôi bối rối ..
Jimmy Hoffa

1
Tại sao nó là đa hình tham số , trái ngược với đa hình đơn giản? Ví dụ với Bất kỳ bạn đã đưa ra, tôi xem nó là một ví dụ nếu đa hình - trong đó các kiểu con có thể được sử dụng thay cho siêu kiểu được chỉ định trong định nghĩa và không phải là đa hình tham số ala C ++ trong đó loại thực tế được chỉ định bởi người lập trình để tạo ra một chức năng mới.
corazza

1
@jcora: Một vài năm cuối, nhưng vì lợi ích của độc giả trong tương lai: nó được gọi là tham số đa hình do tài sản của parametricity , mà phương tiện cho bất kỳ loại bạn cắm vào, tất cả các trường của một hàm như length :: forall a. [a] -> Intphải cư xử như nhau bất kể acủa -nó mờ mịt; bạn không biết gì về nó Không có instanceof(Java genericics) hay kiểu vịt vịt gõ (mẫu C ++) trừ khi bạn thêm các ràng buộc kiểu bổ sung (kiểu chữ Haskell). Với tham số, bạn có thể nhận được một số bằng chứng hay về chính xác những gì một chức năng có thể / không thể làm.
Jon Purdy

8

Bạn có thể tìm thấy các bài báo gốc bằng Google Scholar hoặc CiteSeer - hoặc thư viện trường đại học địa phương của bạn. Đầu tiên là đủ cũ để bạn có thể phải tìm các bản sao của tạp chí, tôi không thể tìm thấy nó trực tuyến. Liên kết mà tôi tìm thấy cho người khác đã bị hỏng, nhưng có thể có liên kết khác. Chắc chắn bạn sẽ có thể tìm thấy những bài báo trích dẫn những điều này.

Hindley, Roger J, Sơ đồ loại chính của một đối tượng trong logic kết hợp , Giao dịch của Hiệp hội toán học Hoa Kỳ, 1969.

Milner, Robin, Một lý thuyết về đa hình loại , Tạp chí khoa học máy tính và hệ thống, 1978.


2
Cái thứ hai có thể được tìm thấy ở đây: citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.67.5276
Magnus


6

Thực hiện suy luận kiểu Hindley-Milner đơn giản trong C #:

Kiểu suy luận Hindley-Milner đối với biểu thức S (Lisp-ish), dưới 650 dòng C #

Lưu ý rằng việc triển khai chỉ nằm trong phạm vi 270 dòng C # (đối với thuật toán W phù hợp và một số cấu trúc dữ liệu để hỗ trợ nó, dù sao đi nữa).

Đoạn trích sử dụng:

    // ...

    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);

    // ...

... mang lại:

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.

Xem thêm triển khai JavaScript của Brian McKenna trên bitbucket, cũng giúp bắt đầu (làm việc cho tôi).

'HTH,

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.