2009-01-21 13 views
15

Ich bin gespannt, wie oft erfahrene Haskell-Programmierer in der Praxis wirklich Typinferenz verwenden. Ich sehe es oft als einen Vorteil gegenüber den immer expliziten Deklarationen, die in bestimmten anderen Sprachen benötigt werden, gelobt, aber aus irgendeinem Grund (vielleicht nur weil ich neu bin) "fühlt" es sich richtig an, eine Typ-Signatur fast immer zu schreiben. und ich bin mir sicher, dass es in einigen Fällen wirklich erforderlich ist.Wann Typinferenz in Haskell ausnutzen?

Können einige erfahrene Haskeller (Haskellites? Haskellizers?) Einige Eingaben liefern?

Antwort

16

Es ist immer noch ein Vorteil, auch wenn Sie Typ-Signaturen schreiben, weil der Compiler in Ihren Funktionen Typfehler feststellt. Normalerweise schreibe ich auch Typensignaturen, lasse sie aber an Stellen wie where oder let Klauseln weg, in denen Sie zwar neue Symbole definieren, aber keine Typensignatur angeben müssen.

Dumme Beispiel mit einer seltsamen Art und Weise Quadrate von Zahlen berechnen:

squares :: [Int] 
squares = sums 0 odds 
    where 
    odds = filter odd [1..] 
    sums s (a:as) = s : sums (s+a) as 

square :: Int -> Int 
square n = squares !! n 

odds und sums Funktionen sind, die eine Art Signatur benötigen würde, wenn der Compiler sich nicht automatisch geschlossen werden würde.

Auch wenn Sie generische Funktionen verwenden, wie Sie normalerweise tun, ist Typ Inferenz, was sicherstellt, dass Sie wirklich alle diese generischen Funktionen in einer gültigen Weise kombinieren. Wenn Sie im obigen Beispiel, sagen

squares :: [a] 
squares = ... 

Der Compiler ableiten kann, dass dies so nicht gültig ist, weil einer der verwendeten Funktionen (die odd Funktion aus der Standardbibliothek), a muss in seine die Typklasse Integral. In anderen Sprachen erkennen Sie dies in der Regel erst zu einem späteren Zeitpunkt.

Wenn Sie dies als Vorlage in C++ schreiben, erhalten Sie einen Compilerfehler, wenn Sie die Funktion für einen nicht-integralen Typ verwenden, aber nicht, wenn Sie die Vorlage definieren. Dies kann ziemlich verwirrend sein, da nicht sofort klar ist, wo Sie einen Fehler gemacht haben, und Sie müssen möglicherweise eine lange Kette von Fehlermeldungen durchsuchen, um die tatsächliche Ursache des Problems zu finden. Und in etwas wie Python erhalten Sie den Fehler zur Laufzeit an einem unerwarteten Punkt, weil etwas nicht die erwarteten Elementfunktionen hatte. Und in noch lockereren Sprachen erhalten Sie möglicherweise keine Fehler, sondern nur unerwartete Ergebnisse.

In Haskell kann der Compiler sicherstellen, dass die Funktion mit allen in der Signatur angegebenen Typen aufgerufen werden kann, auch wenn es sich um eine generische Funktion handelt, die für alle Typen gilt, die bestimmte Einschränkungen erfüllen (auch Klassen genannt). Dies macht es einfach, generisch zu programmieren und generische Bibliotheken zu verwenden, was in anderen Sprachen viel schwieriger ist. Selbst wenn Sie eine generische Typ-Signatur angeben, wird im Compiler immer noch eine Menge von Typ-Inferenzen ausgeführt, um herauszufinden, welcher spezifische Typ bei jedem Aufruf verwendet wird und ob dieser Typ alle Anforderungen der Funktion erfüllt.

5

Ich schreibe immer die Typ-Signatur für Funktionen und Werte auf oberster Ebene, aber nicht für Sachen in "where", "let" oder "do" -Klauseln.

Zuerst werden Funktionen auf oberster Ebene exportiert, und Haddock benötigt eine Typdeklaration, um die Dokumentation zu generieren.

Zweitens, wenn Sie einen Fehler machen, die Compiler-Fehler sind viel einfacher zu dekodieren, wenn der Compiler Typ Informationen verfügbar hat. In der Tat, manchmal in einer komplizierten "where" -Klausel bekomme ich einen unverständlichen Typfehler, also füge ich temporäre Typdeklarationen hinzu, um das Problem zu finden, ein bisschen wie das Typlevel-Äquivalent von printf-Debugging.

Also um die ursprüngliche Frage zu beantworten, verwende ich Typ Inferenz viel, aber nicht 100% der Zeit.

4

Sie haben gute Instinkte. Da sie vom Compiler überprüft werden, geben Signaturen für Top-Level-Werte wertvolle Dokumentation.

Wie andere, habe ich fast immer eine Typ-Signatur für eine Top-Level-Funktion, und fast nie für eine andere Erklärung.

Der andere Ort Typ Rückschluss ist von unschätzbarem Wert bei der interaktiven Schleife (z. B. mit GHCi). Diese Technik ist sehr hilfreich, wenn ich einige neue Funktionen höherer Ordnung oder ähnliches entwerfe und debugge.

1

Wenn Sie mit einem Typprüfungsfehler konfrontiert werden, obwohl der Haskell-Compiler Informationen zu dem Fehler bereitstellt, kann diese Information schwer zu decodieren sein. Um es einfacher zu machen, können Sie die Typensignatur der Funktion auskommentieren und dann sehen, was der Compiler über den Typ herausgefunden hat und wie er sich von Ihrem beabsichtigten Typ unterscheidet.

Eine andere Verwendung ist, wenn Sie eine "innere Funktion" innerhalb einer Top-Level-Funktion konstruieren, aber Sie sind nicht sicher, wie die innere Funktion oder sogar, was sein Typ sein sollte. Sie können die innere Funktion als Argument an die Top-Level-Funktion übergeben und dann ghci nach dem Typ der Typ-Level-Funktion fragen. Dies beinhaltet den Typ der inneren Funktion. Sie können dann ein Tool wie Hoogle verwenden, um zu sehen, ob diese Funktion bereits in einer Bibliothek vorhanden ist.