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.hsund dann wie jede andere Exe ausführen.