Generics sind ein Sprachmerkmal, das die Wiederverwendung von Code ermöglicht.
Zum Beispiel sollte es keine Rolle spielen, welchen Typ eine Liste enthält, um ihre Länge zu berechnen.
Deshalb verwendet die Funktion length einen generischen Typparameter a in ihrer Typsignatur.
length : List a -> Int
Wenn ein Typ generisch über einen anderen Typ ist, verwenden wir einen kleingeschriebenen Namen für diesen anderen Typ und nennen ihn Typparameter.
Häufige Beispiele sind die benutzerdefinierten Typen List, Maybe und Result.
-- Maybe is generic over the type a
type Maybe a
= Nothing
| Just a
someString : Maybe String
someString = Just "hello"
someInt : Maybe Int
someInt = Just 42
Ein wichtiger Hinweis zu Namen von Typparametern: Sie können mehrere Zeichen lang sein. Die einzige Einschränkung ist, dass sie kleingeschrieben sein müssen. Wenn es nützlich ist, geben wir ihnen daher beschreibende Namen.
Ein Typ kann generisch über mehrere andere Typen sein und somit mehrere Typparameter haben. In diesem Fall bekommt jeder unabhängige Typ einen anderen kleingeschriebenen Namen. Unabhängig zu sein hindert sie nicht daran, derselbe Typ zu werden; es ermöglicht ihnen nur, verschieden zu sein.
-- Result is generic over the types error and value
type Result error value
= Ok value
| Err error
Record-Typaliase können ebenfalls Typparameter haben, obwohl das weniger verbreitet ist.
-- Event is generic over the type a
type alias Event a =
{ at : Time
, data : a
}
Da Typen generisch sein können, können es auch Funktionen sein.
Funktionen, die mit generischen Typen umgehen, können geschrieben werden, wie zum Beispiel Maybe.withDefault.
withDefault : a -> Maybe a -> a
withDefault default maybe =
case maybe of
Just value -> value
Nothing -> default
Funktionen, die andere Funktionen als Parameter nehmen, heißen Funktionen höherer Ordnung, und diese können ebenfalls generisch sein.
map : (a -> b) -> Maybe a -> Maybe b
map function maybe =
case maybe of
Just value ->
Just (function value)
Nothing ->
Nothing
In dieser Übung schreibst du eine generische (und magische!) TreasureChest, in der du ein paar Schätze aufbewahrst.
Definiere einen benutzerdefinierten Typ TreasureChest mit einer einzigen Variante.
Die Variante sollte einen zugehörigen Wert vom Typ String haben, mit dem das Passwort der Schatztruhe gespeichert wird.
Die Variante sollte außerdem einen zugehörigen generischen Wert haben, mit dem der Schatz gespeichert wird. Dieser Wert ist generisch, damit der Schatz alles Mögliche sein kann.
Diese Funktion sollte zwei Parameter entgegennehmen
String (zum Ausprobieren eines Passworts)TreasureChest
Diese Funktion sollte den übergebenen Passwortversuch mit dem String in der TreasureChest abgleichen.
Die Funktion sollte ein Maybe zurückgeben.
Wenn die Passwörter übereinstimmen, gib Just mit dem generischen Wert aus der TreasureChest zurück (dem Schatz!).
Wenn die Passwörter nicht übereinstimmen, gib Nothing zurück.
Diese Funktion (höherer Ordnung) sollte zwei Parameter entgegennehmen
TreasureChest, ebenfalls mit demselben generischen TypDiese Funktion sollte die Multiplikatorfunktion mit dem Schatz in der Truhe aufrufen.
Die Funktion sollte eine neue TreasureChest zurückgeben, mit demselben Passwort wie das Original und mit dem multiplizierten Schatz (dem Rückgabewert)
Melde dich bei Exercism an, um Elm mit 28 Konzepte110 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.