Che cos'è Hindley-Milner?


124

Ho incontrato questo termine Hindley-Milner e non sono sicuro di capire cosa significhi.

Ho letto i seguenti post:

Ma non esiste un'unica voce per questo termine in Wikipedia, dove di solito mi offre una spiegazione concisa.
Nota : ne è stato aggiunto uno

Che cos'è?
Quali lingue e strumenti lo implementano o lo usano?
Per favore, offri una risposta concisa?

Risposte:


167

Hindley-Milner è un sistema di tipi scoperto indipendentemente da Roger Hindley (che stava guardando la logica) e in seguito da Robin Milner (che stava guardando i linguaggi di programmazione). I vantaggi di Hindley-Milner sono

  • Supporta funzioni polimorfiche ; ad esempio, una funzione che può fornire la lunghezza dell'elenco indipendentemente dal tipo di elementi o una funzione esegue una ricerca ad albero binario indipendentemente dal tipo di chiavi archiviate nella struttura.

  • A volte una funzione o un valore può avere più di un tipo , come nell'esempio della funzione lunghezza: può essere "elenco di numeri interi a numero intero", "elenco di stringhe a numero intero", "elenco di coppie a numero intero" e così via sopra. In questo caso, un vantaggio del segnale del sistema Hindley-Milner è che ogni termine ben tipizzato ha un tipo "migliore" unico , che è chiamato il tipo principale . Il tipo principale della funzione lunghezza elenco è "per qualsiasi afunzione dall'elenco aall'intero". Ecco aun cosiddetto "parametro di tipo", che è esplicito nel calcolo lambda ma implicito nella maggior parte dei linguaggi di programmazione .polimorfismo parametrico . (Se scrivi una definizione della funzione lunghezza in ML, puoi vedere il parametro type in questo modo:

     fun 'a length []      = 0
       | 'a length (x::xs) = 1 + length xs
    
  • Se un termine ha un tipo Hindley-Milner, il tipo principale può essere dedotto senza la necessità di dichiarazioni di tipo o altre annotazioni da parte del programmatore. (Questa è una benedizione mista, dal momento che chiunque può attestare di aver mai gestito un grosso pezzo di codice ML senza annotazioni.)

Hindley-Milner è la base per il sistema di tipi di quasi tutti i linguaggi funzionali tipicamente statici. Tali lingue di uso comune includono

Tutte queste lingue hanno esteso Hindley-Milner; Haskell, Clean e Objective Caml lo fanno in modi ambiziosi e insoliti. (Le estensioni sono necessarie per gestire le variabili mutabili, poiché Hindley-Milner di base può essere sovvertito usando, ad esempio, una cella mutabile contenente un elenco di valori di tipo non specificato. Tali problemi sono affrontati da un'estensione chiamata restrizione di valore .)

Molte altre lingue e strumenti minori basati su linguaggi funzionali tipizzati usano Hindley-Milner.

Hindley-Milner è una limitazione del Sistema F , che consente più tipi ma che richiede annotazioni da parte del programmatore .


2
@NormanRamsey So che questo è vecchio malvagio, ma grazie per aver chiarito ciò che mi ha infastidito all'infinito: ogni volta che mi riferisco al sistema di tipo hindley-milner qualcuno parla nel parlare di inferenza di tipo al punto che ho iniziato a chiedermi se HM è un tipo sistema o solo l'algoritmo di inferenza ... Grazie, credo sia wikipedia per disinformare le persone su questo punto al punto che mi hanno persino confuso ..
Jimmy Hoffa,

1
Perché è parametricamente polimorfico, al contrario di semplicemente polimorfico? L'esempio con Any che hai dato, lo vedo come un esempio se il polimorfismo - dove si possono usare sottotipi al posto del supertipo specificato nella definizione, e non polimorfismo parametrico ala C ++ dove il programmatore specifica il tipo effettivo per creare un nuova funzione.
corazza,

1
@jcora: con qualche anno di ritardo, ma a beneficio dei futuri lettori: si chiama polimorfismo parametrico a causa della proprietà della parametricità , il che significa che per qualsiasi tipo che si collega, tutte le istanze di una funzione come length :: forall a. [a] -> Intdevono comportarsi allo stesso modo indipendentemente da a- è opaco; non ne sai nulla. Non ci sono instanceof(generici Java) né "typing anatra" (modelli C ++) a meno che non si aggiungano vincoli di tipo extra (caratteri tipografici Haskell). Con la parametricità è possibile ottenere alcune belle prove di ciò che una funzione può / non può fare.
Jon Purdy,

8

Potresti riuscire a trovare i documenti originali utilizzando Google Scholar o CiteSeer o la tua biblioteca universitaria locale. Il primo è abbastanza vecchio che potresti dover trovare copie rilegate del diario, non sono riuscito a trovarlo online. Il collegamento che ho trovato per l'altro era rotto, ma potrebbero essercene altri. Sarai sicuramente in grado di trovare documenti che citano questi.

Hindley, Roger J, Il principale schema di tipo di un oggetto in logica combinatoria , Transactions of the American Mathematical Society, 1969.

Milner, Robin, A Theory of Type Polymorphism , Journal of Computer and System Sciences, 1978.


2
Quest'ultimo può essere trovato qui: citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.67.5276
Magnus,


6

Implementazione di inferenza di tipo Hindley-Milner semplice in C #:

Inferenza di tipo Hindley-Milner sulle espressioni S (Lisp-ish), in meno di 650 righe di C #

Si noti che l'implementazione è nell'intervallo di circa 270 righe o meno di C # (per l'algoritmo W corretto e le poche strutture dati per supportarlo, comunque).

Estratto di utilizzo:

    // ...

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

    // ...

... che produce:

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.

Vedi anche l'implementazione JavaScript di Brian McKenna su bitbucket, che aiuta anche a iniziare (ha funzionato per me).

'HTH,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.