2013-03-16 21 views
12

folgenden Haskell Funktion vor:(Num a) vs Integer Typinferenz

es als gefolgert
sign :: (Num a, Ord a) => a -> Integer 

Statt:

sign a 
    | a < 0 = (-1) 
    | a > 0 = 1 
    | otherwise = 0 

Als ich dies in GHCI ich :t sign zu erwarten laden

Ähnlich, wenn ich nach dem Typ der Ganzzahl 5 frage, erwartete ich Integer, aber stattdessen habe ich

*Main> :t 5 
5 :: Num a => a 

Es gibt etwas, was ich verstehe mich nicht über Haskell-Typen. Die Sache ist, wenn alles, was ich über die Rückkehr Art von sign wissen ist, dass es eine Instanz des Num typeclass ist, dann soll ich nicht in der Lage sein, ihren Rückgabewert in diese Funktion zu übergeben:

double :: Integer -> Integer 
double x = x * 2 

Das heißt, Meine double Funktion erfordert eine Integer, nicht irgendeine Instanz von Num.

Doch die folgenden funktioniert gut:

*Main> double (sign 5.5) 
2 

Was ist das, was ich bin falsch Verständnis über Haskell Typ-System?

+0

Übrigens gibt es natürlich auch die Funktion 'signum :: Num a => a -> a ', d. H. Sie geben eine Zahl hinein und erhalten eine Zahl * des gleichen Typs * zurück. – mrueg

+0

Die Tatsache, dass Sie es an "double" weitergegeben haben, gibt der Typ-Inferenz-Engine mehr Wissen - es weiß, dass es mit "double" funktioniert, und ermittelt, wie das möglich gemacht wird. – jpaugh

Antwort

18

Die Sache ist, wenn alles, was ich über den Rückgabetyp ‚Zeichen‘ wissen ist, dass es eine Instanz des Num typeclass ist, dann soll ich nicht in der Lage sein, um den Rückgabewert in diese Funktion übergeben:

Richtig, wenn das alles wäre, was du wüsstest, könntest du es nicht an double weitergeben.

Aber der Typ

sign :: (Num a1, Num a, Ord a1) => a1 -> a 

bedeutet, dass der Ergebnistyp von sign ist je nachdem, wasNum Typ der Anrufer verlangt. Typvariablen in Typ-Signaturen sind (implizit) universell quantifiziert, nicht existentiell, wie z.B. Java-Schnittstellen

sign kann einen Rückgabewert beliebigen Typs erzeugen, abhängig von der Einschränkung ist es eine Instanz von Num, und der zurückgegebene Typ wird vom aufrufenden Kontext bestimmt.

Wenn der Anrufer eine Integer will, wird es eins. Wenn es ein Double will, kein Problem auch nicht.

ich zunächst vergessen zu erwähnen:

Und falls ich für die Art der ganzen Zahl 5, frage ich erwartete "Integer", aber stattdessen habe ich

*Main> :t 5 
    5 :: Num a => a 

Zahlenliterale sind polymorph, ein Integer-Literal steht für fromInteger value und ein Bruchwort für fromRational value.

+0

Ah, deine Bearbeitungen haben meine Antwort etwas überflüssig gemacht ... –

+0

Etwas. Trotzdem, lass es stehen, es ist gut, eine alternative Formulierung zu haben. –

+0

Vielen Dank für diese Antwort! Dies erklärt die Dinge ein wenig für mich ... Ich habe vergessen, dass der Rückgabetyp "Num a" ist - Num, wobei "a" alles sein kann (mit der Maßgabe, dass "Num a" -Typkonstruktor existiert). –

6

In Haskell, wenn eine Funktion zurückgibt Typ x das Ergebnis ist, das bedeutet, dass die Anrufer auswählen können, was x, nicht die Funktion sein sollte. Eher, muss die Funktion in der Lage sein, jeden möglichen Typ zurückzugeben.

Ihre sign kann jede Art von Daten zurückgeben - einschließlich Integer. Die double Funktion will eine Integer, also ist es in Ordnung - sign kann das zurückgeben.

Ein weiterer Teil des Puzzles Sie nicht bewusst sein kann: hat int Typ In Java 2 und 2.0 hat double geben. Aber in Haskell, 2 hat Typ Num x => x - mit anderen Worten, jede mögliche Nummer Typ. (Auch 2.0 hat Typ , was ein ähnliches Geschäft ist.)

10

Ich wollte nur @ DanielFischer Antwort ein wenig klären. Eine Typ-Signatur wie f :: Num b => a -> b bedeutet, dass feine beliebige Instanz der Typenklasse Num zurückgeben kann. Wenn f aufgerufen wird, verwendet Haskell den Kontext (die Typsignatur des Aufrufers), um den konkreten Typ b zu bestimmen.

Darüber hinaus sind Haskells numerische Literale ein Beispiel für diese Art von Polymorphie. Deshalb gab :t 5 Ihnen Num a => a. Das Symbol 5 kann als beliebige Art von Nummer, nicht nur eine ganze Zahl handeln. Der Kontext, in dem es erscheint, bestimmt, was es sein wird.

+0

Um es genauer zu sagen (aber weniger lesbar): 'f' kann jeden Wert zurückgeben, dessen Typ eine Instanz von' Num' ist. Der Kontext des Aufrufs von "f" wird verwendet, um den konkreten _Typ_ von "b" zu bestimmen; der zurückgegebene _value_ hängt natürlich von 'f' und den Eingabedaten ab. –