Vorlesung: Praxis der Funktionalen Programmierung | Index

Die Außenwelt - IO

Denken wir zurück an die Zustandsmonade (beim Baum-numerieren)
data ST a = ST (Int -> (a, Int))
Ihr Vorteil war, daß sie den Zustand vor dem Nutzer versteckt (kapselt) und nur wenige Operationen erlaubt (exportiert).

Das gibt uns die Möglichkeit, Operationen, die Wirkungen auf die "wirkliche Welt" haben (z. B. Eingabe, Ausgabe, File-Operation) als Aktionen in einer Zustandsmonade aufzufassen. Die Zustandsvariable ist die Welt, aber durch die Kapselung sehen wir sie nicht explizit. Wir können also damit auch keinen Unfug anstellen (etwa die Welt löschen oder vervielfachen).

Diese Monade heißt IO. Das ist, wie sich das gehört, ein Typkonstruktor. Ein f :: IO a ist also eine IO-Aktion, die (möglicherweise) eine Wirkung auf die Welt hat, und ein Ergebnis :: a. Beispiele:

getLine :: IO String
putStrLn :: String -> IO ()
in der zweiten Zeile haben wir den häufigen Fall, daß uns das Ergebins nicht interessiert (deswegen ()), wohl aber die Wirkung (das Drucken der Zeile).

Mit der IO-Monade kann man nun imperativ funktional programmieren. Schreiben wir der Übung halber einen Ersatz für die Unix-Funktion "cat":

module Main (main) 
-- ein Hauptprogramm muß  main :: IO () exportieren
-- vergleiche  main ()  in einem C-Programm
where

import Prelude hiding (sequence, sequence_, (>>)) -- siehe unten

import IO -- openFile usw.
import System (getArgs)

cat :: String -> IO ()
cat fname = do

   -- die Typen stehen nur aus didaktischen Gründen dort
   h <- openFile fname ReadMode :: IO Handle

   -- liest alles auf einmal, aber lazy
   cs <- hGetContents h :: IO String

   -- schreibt auf stdout
   putStr cs :: IO ()

   hClose h

main = do
    argv <- getArgs :: IO [ String ]
    sequence_ [ cat arg | arg <- argv ]

-- Die Fuktion sequence_ steht eigentlich in der Prelude.
-- Wir definieren sie über eine Variante von (>>=),
-- die das Ergebnis der ersten Rechnung ignoriert:

(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= const y

sequence_ :: Monad m => [ m a ] -> m ()
sequence_ = foldr (>>) (return ())

-- es gibt noch die Variante, die sich alle Ergebnisse merkt:
sequence :: Monad m => [ m a ] -> m [ a ]
sequence [] = return []
sequence (x : xs) = 
   do { z <- x; zs <- sequence xs; return (z : zs) }


Da kann man kaum noch erkennen, ob es nun Haskell oder C ist.

Ein solches Hauptprogramm können wir entweder separat unter hugs ausführen

runhugs Cat.hs arg1 arg2 ...
oder kompilieren (wenn wir einen Compiler haben)
ghc -o Cat Cat.hs
und dann wie jede andere Exe ausführen.

Aufgabe

Implementiere einen Ersatz für die Unix-Funktion "wc" (word count, zählt Zeichen, Wörter und Zeilen, und addiert das schließlich für alle Argumente).

best viewed with any browser


http://www.informatik.uni-leipzig.de/~joe/ mailto:joe@informatik.uni-leipzig.de