Personal tools

De/Flussrichtung

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(first raw version)
 
(Markup)
Line 1: Line 1:
Wenn man sich mal genauer umschaut, gibt es in Haskell beide Varianten gleich haeufig.
+
Wenn man sich mal genauer umschaut, gibt es in Haskell beide Varianten gleich häufig.
 
Wir haben die Schreibweisen:
 
Wir haben die Schreibweisen:
links nach rechts:
 
Funktionsdefinition f x =3D x*x (links Eingabe, rechts Ausgabe)
 
Lambda \ x -> x*x
 
do-notation do f; g
 
monadische Verkettung f >>=3D g
 
   
rechts nach links:
+
links nach rechts:
Funktionsanwendung f x
+
{|
f $ x
+
| Funktionsdefinition || <hask> f x = x*x </hask> (links Eingabe, rechts Ausgabe)
Verkettung g . f
+
|-
Ergebnisse von Monaden do x <- f
+
| Lambda || <hask> \ x -> x*x </hask>
monadische Verkettung g =3D<< f
+
|-
  +
| do-notation || <hask> do f; g </hask>
  +
|-
  +
| monadische Verkettung || <hask> f >>= g </hask>
  +
|}
   
Lambda-Ausdruecke saehen viel natuerlicher aus, wenn die Verkettung
+
rechts nach links:
von links nach rechts gehen wuerde, also statt
+
{|
transform3 . (\x -> transform2 x x) . transform1
+
| Funktionsanwendung || <hask> f x </hask>, <hask> f $ x </hask>
besser
+
|-
transform1 . (\x -> x x transform2) . transform3
+
| Verkettung || <hask> g . f </hask>
  +
|-
  +
| Ergebnisse von Monaden || <hask> do x <- f </hask>
  +
|-
  +
| monadische Verkettung || <hask> g =<< f </hask>
  +
|}
  +
  +
Lambda-Ausdrücke sähen viel natürlicher aus, wenn die Verkettung
  +
von links nach rechts gehen würde, also statt
  +
<hask> transform3 . (\x -> transform2 x x) . transform1 </hask>
  +
besser
  +
<hask> transform1 . (\x -> x x transform2) . transform3 </hask>
   
 
Auch
 
Auch
do f -> x; g x
+
<hask> do f -> x; g x </hask>
wre schon viel nher an der darunterliegenden Implementierung
+
wäre schon viel näher an der darunterliegenden Implementierung
f >>=3D \x -> g x
+
<hask> f >>= \x -> g x </hask>
   
   
Allerdings sehe ich noch zwei Unschoenheiten/Gewoehnungsbeduerftigkeiten:
+
Allerdings sehe ich noch zwei Unschönheiten/Gewöhnungsbedürftigkeiten:
   
Eines tritt beim Lesen von links-nach-rechts-Ausdruecken auf: Man sieht
+
Eines tritt beim Lesen von links-nach-rechts-Ausdrücken auf: Man sieht
bei der Verwendung von Funktionen hoeherer Ordnung nicht sofort, wo die
+
bei der Verwendung von Funktionen höherer Ordnung nicht sofort, wo die
Argumente eines Funktionsaufrufes aufhoeren. Mein Lieblingsbeispiel - der
+
Argumente eines Funktionsaufrufes aufhören. Mein Lieblingsbeispiel - der
Differentialoperator 'derive':
+
Differentialoperator <hask>derive</hask>:
derive :: (a -> a) -> (a -> a)
+
<hask> derive :: (a -> a) -> (a -> a) </hask>
   
Wenn man den Ausdruck 'x f derive' nur bis 'f' liest, denkt man, es
+
Wenn man den Ausdruck <hask> x f derive </hask> nur bis <hask> f </hask> liest, denkt man, es
handelt sich um die Anwendung von f auf x und danach kommt noch irgendwas.
+
handelt sich um die Anwendung von <hask> f </hask> auf <hask> x </hask> und danach kommt noch irgendwas.
Aber nein, durch 'derive' wird alles umgekrempelt. Man muss also einen
+
Aber nein, durch <hask> derive </hask> wird alles umgekrempelt.
Ausdruck erst bis zum naechsten Absatz (schliessende Klammer,
+
Man muss also einen Ausdruck erst bis zum nächsten Absatz
Infixoperator) lesen, bis man weiss, um welchen Funktionsaufruf es sich
+
(schließende Klammer, Infixoperator) lesen,
eigentlich handelt.
+
bis man weiß, um welchen Funktionsaufruf es sich eigentlich handelt.
   
Die zweite Sache ist die Verbindung von Typsignatur und
+
Die zweite Sache ist die Verbindung von Typsignatur und Funktionsanwendung.
Funktionsanwendung. Wir schreiben
+
Wir schreiben
  +
<haskell>
 
derive :: (a -> a) -> (a -> a)
 
derive :: (a -> a) -> (a -> a)
 
derive f :: a -> a
 
derive f :: a -> a
 
derive f x :: a
 
derive f x :: a
Wir koennen gewissermassen die Argumente von der Typseite auf die
+
</haskell>
  +
Wir können gewissermaßen die Argumente von der Typseite auf die
 
Werteseite umschichten. Die Argumentreihenfolge ist daher in
 
Werteseite umschichten. Die Argumentreihenfolge ist daher in
 
Typsignatur und Funktionsanwendung gleich. Bei der konsequenten
 
Typsignatur und Funktionsanwendung gleich. Bei der konsequenten
 
links-nach-rechts-Notation ist das nicht mehr der Fall:
 
links-nach-rechts-Notation ist das nicht mehr der Fall:
  +
<haskell>
 
derive :: (a -> a) -> (a -> a)
 
derive :: (a -> a) -> (a -> a)
 
f derive :: a -> a
 
f derive :: a -> a
 
x f derive :: a
 
x f derive :: a
Die Notation hat natuerlich den unbestreitbaren Vorteil, dass man sich
+
</haskell>
'::' wie ein Gleichheitszeichen vorstellen kann, wo man auf beiden Seiten
+
Die Notation hat natürlich den unbestreitbaren Vorteil, dass man sich
  +
<hask>::</hask> wie ein Gleichheitszeichen vorstellen kann, wo man auf beiden Seiten
 
der Gleichung die Ausdruecke immer von links manipuliert.
 
der Gleichung die Ausdruecke immer von links manipuliert.
  +
  +
[[Category:Syntax]]

Revision as of 14:37, 19 March 2007

Wenn man sich mal genauer umschaut, gibt es in Haskell beide Varianten gleich häufig. Wir haben die Schreibweisen:

links nach rechts:

Funktionsdefinition
 f x = x*x
(links Eingabe, rechts Ausgabe)
Lambda
 \ x -> x*x
do-notation
 do f; g
monadische Verkettung
 f >>= g

rechts nach links:

Funktionsanwendung
 f x
,
 f $ x
Verkettung
 g . f
Ergebnisse von Monaden
 do x <- f
monadische Verkettung
 g =<< f

Lambda-Ausdrücke sähen viel natürlicher aus, wenn die Verkettung von links nach rechts gehen würde, also statt

 transform3 . (\x -> transform2 x x)  . transform1

besser

 transform1 . (\x -> x x transform2)  . transform3

Auch

 do f -> x; g x

wäre schon viel näher an der darunterliegenden Implementierung

 f >>= \x -> g x


Allerdings sehe ich noch zwei Unschönheiten/Gewöhnungsbedürftigkeiten:

Eines tritt beim Lesen von links-nach-rechts-Ausdrücken auf: Man sieht bei der Verwendung von Funktionen höherer Ordnung nicht sofort, wo die Argumente eines Funktionsaufrufes aufhören. Mein Lieblingsbeispiel - der

Differentialoperator
derive
:
 derive :: (a -> a) -> (a -> a)
Wenn man den Ausdruck
 x f derive
nur bis
 f
liest, denkt man, es handelt sich um die Anwendung von
 f
auf
 x
und danach kommt noch irgendwas. Aber nein, durch
 derive
wird alles umgekrempelt.

Man muss also einen Ausdruck erst bis zum nächsten Absatz (schließende Klammer, Infixoperator) lesen, bis man weiß, um welchen Funktionsaufruf es sich eigentlich handelt.

Die zweite Sache ist die Verbindung von Typsignatur und Funktionsanwendung. Wir schreiben

 derive :: (a -> a) -> (a -> a)
 derive f :: a -> a
 derive f x :: a
 Wir können gewissermaßen die Argumente von der Typseite auf die

Werteseite umschichten. Die Argumentreihenfolge ist daher in Typsignatur und Funktionsanwendung gleich. Bei der konsequenten links-nach-rechts-Notation ist das nicht mehr der Fall:

     derive :: (a -> a) -> (a -> a)
   f derive :: a -> a
 x f derive :: a
 Die Notation hat natürlich den unbestreitbaren Vorteil, dass man sich
::
wie ein Gleichheitszeichen vorstellen kann, wo man auf beiden Seiten

der Gleichung die Ausdruecke immer von links manipuliert.