[Inhalt]...[Strikte Auswertung]...[Vor-/Nachteile]
Die Funktion erwartet als Parameter einen Ausdruck a und einen Funktionsausdruck (b->b), der auf a angewendet werden soll.
Laut dem "Haskell 98 Report" ist ihr Wert folgendermaßen definiert:
Das Zeichen "_|_" steht für einen nicht terminierenden Ausdruck (wie die Division durch null). Ist also der
Ausdruck, auf den die Funktion angewendet werden soll, nicht definiert, so ist auch das Ergebnis der "
Code-Beispiel in Haskell [Quelldatei]
Die "Eq"-/ "Show"-Instanzen bleiben unverändert.
Durch die Verwendung des "
Dieser Gleichheitstest führt zu einem Laufzeitfehler, weil der Ausdruck (2/0) bei Konstruktion
des (zweiten) Vektors strikt ausgewertet wird.
Code-Beispiel in Haskell [Quelldatei]
Das Verhalten entspricht dem "Call-by-value"-Beispiel.
4.1 Erzwungene Auswertung
Die Auswertung von Teilausdrücken muss aufgrund der "Lazy evaluation" in "Haskell"
unter Umständen erzwungen werden ("Force strictness"). Da standardmäßig nicht strikt ausgewertet
wird, steht hierfür in "Haskell" die Funktion "seq
" zur Verfügung.
>> seq :: a->(b->b)
>> seq _|_ f = _|_
>> seq x f = f x
(Pseudocode)seq
"
-Funktion undefiniert. Andernfalls ist das Ergebnis abhängig von dem Ausdruck (b->b). Die "seq
"-Funktion
kann als normale Funktion seq x (f x)
oder als Infix-Operator x `seq` f x
benutzt werden.
Bei zusammengesetzten Datentypen muss dabei beachtet werden, dass "seq
" nur die jeweils erste Ebene eines
Ausdrucks (also z.B. bei einer Liste den Kopf) auswertet.
Für geschachtelte Ausdrücke kann zudem der Alias "$!
" verwendet werden:
>> $! :: (a->b) -> a -> b
>> f $! x = x 'seq' f x
4.2 Simulation von "Call-by-value"
Mit Hilfe der "seq
"-Methode kann in "Haskell" das Prinzip
von "Call-by-value" simuliert werden. Zu Demonstrationszwecken soll noch einmal
das Code-Beispiel aus Kapitel 2 "Nicht-strikte Auswertung" herangezogen werden.
Ein neuer Typ mit einer veränderten Konstruktor-Funktion, welche die aktuellen Parameter strikt
auswertet, soll implementiert werden.
>> data SVect3D = SVect3f {u,v,w::Float}
>> newsvect x y z = ((SVect3f $! x) $! y) $! z
>> instance Show SVect3f
>> where show n = show(u n)++" "++show(v n)++" "++show(w n)
>> instance Eq SVect3f
>> where (==) a b = ((u a)==(u b)&&(v a)==(v b)&&(w a)==(w b))
$!
"-Operators werden bei Erzeugen eines neuen
Ausdrucks vom Typ "SVect3f
" alle drei übergebenen Float-Audrücke ausgewertet.
Ein Vergleich, wie er in Kapitel 2 mit "Lazy Evaluation" ausgewertet wurde, schlägt nun fehl:
>> (newsvect (1+2) 2 4)==(newsvect 2 (2/0) 4) => _|_
4.3 "Strictness flags"
Das Beispiel für die Simulation von "Call-by-value" zeigt einen typischen Fall,
in dem eine strikte Auswertung von Teilausdrücken gewünscht ist. Es kann sinnvoll
sein, sicherzustellen, dass bei Konstruktion eines neuen Ausdruck eines bestimmten
Typs alle Teilausdrücke terminieren (definiert sind). Für diesen Sonderfall existiert in
"Haskell" ein weiteres syntaktisches Element, mit dem Parameter von Typ-Konstruktoren
als strikt gekennzeichnet werden. Ein Ausrufezeichen vor der
Typangabe des formellen Parameters im Konstruktor erfüllt diesen Zweck.
Mit Hilfe dieser "strictness flags" kann der "Call-by-value"-Konstruktor
abgekürzt implementiert werden. Dazu noch einmal das Beispiel mit dem Vektor-Typ:
>> data SFVect3D = SFVect3f !Float !Float !Float
>> newsfvect f g h = SFVect3f f g h