Zur Erinnerung: mit Hilfe des abstrakten Typs `IO val' werden Aktionen beschrieben, die Ein- oder Ausgaben vornehmen. Allgemeiner: ..., die mit dem Betriebssystem oder der Umgebung kommunizieren.
---------------------------------------------------------------------- > data IO val ----------------------------------------------------------------------
Ein Element vom Typ `IO val' beschreibt eine EA-Aktion, die einen Wert vom Typ `val' zurückgibt.
Vordefinierte Ausgabefunktionen (teilweise aus der Bibliothek `IOSupport'):
---------------------------------------------------------------------- > putChar :: Char -> IO () > putStr :: String -> IO () > putLine :: String -> IO () -- IOSupport > print :: (Text a) => a -> IO () ----------------------------------------------------------------------
Vordefinierte Eingabefunktionen (teilweise aus der Bibliothek `IOSupport'):
---------------------------------------------------------------------- > getChar :: IO Char > getLine :: IO String -- IOSupport > readLine :: (Text a) => IO a -- IOSupport ----------------------------------------------------------------------
Vordefinierte Operationen auf Dateien.
---------------------------------------------------------------------- > type FilePath = String > > readFile :: FilePath -> IO String > writeFile :: FilePath -> String -> IO () > appendFile :: FilePath -> String -> IO () ----------------------------------------------------------------------
Beachte: `readFile' arbeitet `lazy': Der Einleseprozeß wird erst durch den Zugriff auf die Zeichenkette angestoßen.
Verknüpfung von EA-Aktionen:
---------------------------------------------------------------------- > done :: IO () -- IOSupport > return :: a -> IO a > (>>) :: IO a -> IO b -> IO b > (>>=) :: IO a -> (a -> IO b) -> IO b ----------------------------------------------------------------------
C-Programmierer. `return' bleibt `return'; `act1 >> act2' läßt sich als `act1; act2' lesen und `act1 >>= \x -> act2' als `x = act1; act2'.
Jedes Haskell Programm muß den Bezeichner `main :: IO ()' definieren.
---------------------------------------------------------------------- > main = print [ (i, i^2) | i <- [0 .. 9] ] ----------------------------------------------------------------------
Beim Programmstart wird die an `main' gebundene Aktion (respektive die Beschreibung der Aktion) ausgeführt.
Beispiel: Das Programm fragt den Benutzer nach einem Dateinamen und gibt den Inhalt der Datei auf dem Bildschirm aus.
---------------------------------------------------------------------- > askFor :: String -> IO String > askFor s = putStr s >> getLine > > main :: IO () > main = askFor "filename: " >>= \file -> > readFile file >>= \cnts -> > putStr cnts ----------------------------------------------------------------------
Die Funktionen `sequence' und `accumulate' führen Listen von Aktionen aus.
---------------------------------------------------------------------- > sequence :: [IO a] -> IO () > sequence [] = done > sequence (a:as) = a >> sequence as > > accumulate :: [IO a] -> IO [a] > accumulate [] = return [] > accumulate (a:as) = a >>= \v -> > accumulate as >>= \vs -> > return (v:vs) ----------------------------------------------------------------------
`sequence' und `accumulate' werden gerne mit Listenbeschreibungen kombiniert.
---------------------------------------------------------------------- > askForMany :: [String] -> IO [String] > askForMany xs = accumulate [ askFor s | s <- xs ] ----------------------------------------------------------------------
Die Bibliothek `LibSystem' definiert Operationen, um mit dem Betriebssystem zu kommunizieren.
---------------------------------------------------------------------- > data ExitCode = ExitSuccess | ExitFailure Int > > getArgs :: IO [String] > getProgName :: IO String > getEnv :: String -> IO String > system :: String -> IO ExitCode > exitWith :: ExitCode -> IO a ----------------------------------------------------------------------
EA-Aktionen können fehlschlagen: eine Datei ist z.B. nicht vorhanden oder nicht lesbar. Derartige Fehler können abgefangen oder auch selbst ausgelöst werden.
---------------------------------------------------------------------- > data IOError13 = AlreadyExists String > | ... > | NoSuchThing String > | ... > | UserError String > | EOF > > failWith :: IOError13 -> IO a > handle :: IO a -> (IOError13 -> IO a) -> IO a ----------------------------------------------------------------------
Abgeleitete Operationen:
---------------------------------------------------------------------- > fail :: String -> IO a > fail = failwith . UserError > > try :: IO a -> IO (Either IOError13 a) > try p = (p >>= (return . Right)) > `handle` (return . Left) > > either :: (a -> c) -> (b -> c) > -> (Either a b) -> c > either f g (Left x) = f x > either f g (Right x) = g x ----------------------------------------------------------------------
Typische Anwendung von `try' und `either':
----------------------------------------------------------------------
try a >>= either (\err -> ...) (\ok -> ...)
----------------------------------------------------------------------
Beispiel: Ausgabe einer Datei mit Fehlerbehandlung.
---------------------------------------------------------------------- > askFor :: String -> IO String > askFor s = putStr s >> > getLine `handle` \err -> > case err of > EOF -> exitWith ExitSuccess > _ -> failWith err > > main = askFor "filename: " >>= \file -> > try (readFile file) >>= > either > (\err -> case err of > NoSuchThing _ -> > putLine "file does not exist" > _ -> failWith err) > (\cnts -> putStr cnts) ----------------------------------------------------------------------
Beispiel: Realisierung des UNIX-Kommandos `echo'.
NAME
echo - display a line of text
SYNOPSIS
echo [-n] [string ...]
Haskell Programm:
----------------------------------------------------------------------
> import LibSystem
>
> main = getArgs >>= echo
>
> echo [] = done
> echo ("-n":x) = putStr (unwords x)
> echo x = putLine (unwords x)
----------------------------------------------------------------------
Beispiel: Realisierung des UNIX-Kommandos `cat' (vereinfacht).
NAME
cat - concatenate files and print on the standard output
SYNOPSIS
cat [-n] [file...]
Haskell Programm:
----------------------------------------------------------------------
> import Support
> import IOSupport
> import LibSystem
>
> main = getArgs >>= cat
>
> cat :: [String] -> IO ()
> cat [] = done
> cat ("-n":x) = sequence [ copy True f | f<-x ]
> cat x = sequence [ copy False f | f<-x ]
>
> copy :: Bool -> FilePath -> IO ()
> copy b f = readFile f >>= \cnts ->
> sequence [
> onlyif b (putLineNo n) >>
> putLine l
> | (n, l)<-zip [1.. ] (lines cnts) ]
>
> putLineNo :: Int -> IO ()
> putLineNo n = putStr (rjustify 6 (show n) ++ " ")
>
> onlyif :: Bool -> IO () -> IO ()
> onlyif b a = if b then a else done
----------------------------------------------------------------------
Go to the first, previous, next, last section, table of contents.