Aus den Problemen, die viele Programmierer mit Dialogen hatten, wurde
das monadische I/O-Konzept entwickelt. [PJW93] Es basiert auf
eine Programmiermethode, die in geschickter Weise Daten implizit
weiterreicht, den Monaden. [Wad92][Wad90]
Eine Sequentialisierung kann in rein funktionalen Sprachen nur durch
Funktionakomposition adequat erreicht werden. Haben wir zwei I/O
Funktionen
und
, die nacheinander ausgeführt werden sollen,
so ist dieses am einfachsten zu erreichen, wenn
einen Wert hat den
als Eingabeargument zur Evaluierung benötigt. Wenn dieser Wert
von
dann in irgendeiner Weise noch die Umgebung (Filesystem etc)
darstellt, dann ist auch die referentielle Transparenz gewährleistet.
Diesem Gedanken liegt das monadische I/O zugrunde. Monaden stellen
zwei Operatoren zur Verfügung: bind ein Sequenzoperator,
der durch Komposition
realisiert wird, und unit einen Operator, der es erlaubt
komplexe Datentypen in eine Monade zusammenzupacken, die dann implizit
weitergereicht werden. Es kann also gut die für I/O benötigte Umgebung
(die World) in einer Monade enthalten sein und weitergereicht
werden.
Betrachten wir die beiden Aspekte des monadischen I/O getrennt:
wort :: IO String
wort = wortaux []
wortaux String -> IO String
wortaux w = getcIO `bindIO` \a ->
if ((a== eof) \/ (a==' ')) then
unitIO (reverse w)
else
(unitIO (a:w)) `bindIO` wortaux
unitIO a w = MkIORes a w
bindIO m k w = case (m w) of
MkIORes a w' -> k a w'
Der Typ World ist ein
abstrakter Typ mit genau einer auf ihm definierten Funktion, der
Funktion ccall. Diese Funktion ist das einzige Sprachkonstrukt,
daHaskell zum Implementieren des monadischen I/O hinzugefügt wurde.
Es erlaubt eine beliebige C-Funktion aufzurufen, welche dann die
Kommunikation mit der Umgebung betreibt. Mit Hilfe dieser Funktion
werden die I/O-Funktionen definiert. Betrachten wir hierzu ein Beispiel:
putcIO a =\w ->
case a of
MkChar a# ->
case (ccall# putchar a# w) of
MkIORes# n# w' -> MkIORes () w'