2015-10-24 6 views

Antwort

14

type erklärt Synonym. Ein Typ-Synonym ist ein neuer Name für einen vorhandenen Typ. Zum Beispiel ist dies, wie Stringin the standard library definiert:

type String = [Char] 

String ein anderer Name für eine Liste von Char s ist. GHC wird alle Verwendungen von String in Ihrem Programm mit [Char] zur Kompilierzeit ersetzen.

Um klar zu sein, ein Stringist buchstäblich eine Liste von Char s. Es ist nur ein Alias. Sie können alle Standard-Listenfunktionen auf String Werte verwenden:

-- length :: [a] -> Int 
ghci> length "haskell" 
7 
-- reverse :: [a] -> [a] 
ghci> reverse "functional" 
"lanoitcnuf" 

data eine Art neue Daten erklärt, die im Gegensatz zu einer Art Synonym, unterscheidet sich von jedem anderen Typ ist. Datentypen haben eine Anzahl von Konstruktoren definieren die möglichen Fälle Ihres Typs. Zum Beispiel ist dies, wie Boolin the standard library definiert:

data Bool = False | True 

A Bool Wert entweder True oder False sein kann. Die Datentypen unterstützen Mustererkennung, sodass Sie eine Laufzeit-Fallanalyse für einen Wert eines Datentyps durchführen können.

yesno :: Bool -> String 
yesno True = "yes" 
yesno False = "no" 

data Typen mehrere Konstruktoren (wie bei Bool) haben können, können durch andere Arten parametriert werden, können andere Typen in ihnen enthalten können, und sich selbst rekursiv beziehen. Hier ist ein Modell von Ausnahmen, das dies zeigt; Ein Error a enthält eine Fehlermeldung des Typs a und möglicherweise den Fehler, der es verursacht hat.

data Error a = Error { value :: a, cause :: Maybe Error } 
type ErrorWithMessage = Error String 

myError1, myError2 :: ErrorWithMessage 
myError1 = Error "woops" Nothing 
myError2 = Error "myError1 was thrown" (Just myError1) 

Es ist wichtig zu erkennen, dass data eine neue Art erklärt, die voneinander im System von einem anderen Typ ist.Wenn String als data Typ mit eine Liste von Char s deklariert wurde (anstelle eines Synonyms), könnten Sie keine Listenfunktionen darauf verwenden.

data String = MkString [Char] 
myString = MkString ['h', 'e', 'l', 'l', 'o'] 
myReversedString = reverse myString -- type error 

Es gibt eine weitere Vielzahl von Typdeklaration: newtype. Dies funktioniert eher wie eine data Deklaration - es führt einen neuen Datentyp ein, der von jedem anderen Typ getrennt ist, und kann pattern-matched sein - außer dass Sie auf einen einzelnen Konstruktor mit einem einzelnen Feld beschränkt sind. Mit anderen Worten, ein newtype ist ein data Typ, der einen bestehenden Typ umschließt.

Der wesentliche Unterschied ist die Kosten ein newtype: der Compiler verspricht, dass ein newtype in der gleichen Art und Weise wie die Art dargestellt wird, es umschließt. Es gibt keine Laufzeitkosten für das Packen oder Entpacken von newtype. Dies macht newtype s nützlich für die Herstellung administrative (anstatt strukturelle) Unterschiede zwischen den Werten.

newtype s interagieren gut mit Typklassen. Betrachten Sie zum Beispiel Monoid, die Klasse von Typen mit einer Möglichkeit, Elemente (mappend) und ein spezielles "leeres" Element (mempty) zu kombinieren. Int kann in eine Monoid in vielerlei Hinsicht gemacht werden, einschließlich der Addition mit 0 und Multiplikation mit 1. Wie können wir wählen, welche für eine mögliche Monoid Instanz von Int zu verwenden? Es ist besser, keine Präferenz anzugeben und newtype s zu verwenden, um entweder die Verwendung ohne Laufzeitkosten zu aktivieren. Paraphrasiert the standard library:

-- introduce a type Sum with a constructor Sum which wraps an Int, and an extractor getSum which gives you back the Int 
newtype Sum = Sum { getSum :: Int } 
instance Monoid Sum where 
    (Sum x) `mappend` (Sum y) = Sum (x + y) 
    mempty = Sum 0 

newtype Product = Product { getProduct :: Int } 
instance Monoid Product where 
    (Product x) `mappend` (Product y) = Product (x * y) 
    mempty = Product 1 
1

Mit data Sie neuen Datentyp erstellen und einen Konstruktor für sie erklären:

data NewData = NewDataConstructor 

Mit type definieren Sie einfach einen Alias:

type MyChar = Char 

Im type Fall, dass Sie Wert passieren kann MyChar Typ zu Funktion erwartet eine Char und umgekehrt, aber Sie können dies für data MyChar = MyChar Char nicht tun.

2

type funktioniert wie let: ermöglicht es Ihnen, einen wiederverwendbaren Namen etwas zu geben, aber dass etwas funktioniert immer so, als ob Sie die Definition inlined hatten. So

type ℝ = Double 

f :: ℝ -> ℝ -> ℝ 
f x y = let x2 = x^2 
     in x2 + y 

verhält sich genauso wie

f' :: Double -> Double -> Double 
f' x y = x^2 + y 

wie in: Sie überall in Ihrem Code f mit f' und umgekehrt ersetzen kann; nichts würde sich ändern.

OTOH, beide data und newtype erzeugen eine undurchsichtige Abstraktion. Sie sind eher wie ein Klassenkonstruktor in OO: Obwohl ein Wert einfach in Bezug auf eine einzige Nummer implementiert ist, verhält es sich nicht notwendigerweise verhalten wie eine solche Zahl. Zum Beispiel

newtype Logscaledℝ = LogScaledℝ { getLogscaled :: Double } 

instance Num LogScaledℝ where 
    LogScaledℝ a + LogScaledℝ b = LogScaledℝ $ a*b 
    LogScaledℝ a - LogScaledℝ b = LogScaledℝ $ a/b 
    LogScaledℝ a * LogScaledℝ b = LogScaledℝ $ a**b 

Obwohl hier Logscaledℝ ist datenweise immer noch nur eine Double Zahl verhält es sich deutlich von Double.