Kurz: Statt null gibt es Maybe a: entweder Just wert oder Nothing. Der Compiler zwingt dich, beide Fälle zu behandeln.
Teil des Kurses Haskell
Maybe: Werte, die fehlen können
Statt null gibt es Maybe a: entweder Just wert oder Nothing. Der Compiler zwingt dich, beide Fälle zu behandeln.
import Data.Maybe (fromMaybe, mapMaybe, catMaybes, isJust, fromJust, listToMaybe, maybe)
import Text.Read (readMaybe)
import qualified Data.Map as M
sicherDiv :: Int -> Int -> Maybe Int
sicherDiv _ 0 = Nothing
sicherDiv a b = Just (a `div` b)
main :: IO ()
main = do
print (sicherDiv 10 2, sicherDiv 1 0)
print (fromMaybe 0 (sicherDiv 1 0))
print (maybe "nichts" show (sicherDiv 9 3))
print (readMaybe "42" :: Maybe Int, readMaybe "x" :: Maybe Int, readMaybe "3.5" :: Maybe Double)
print (mapMaybe (\s -> readMaybe s :: Maybe Int) ["1", "x", "3"])
print (catMaybes [Just 1, Nothing, Just 3])
print (lookup "b" [("a", 1), ("b", 2)], M.lookup 5 (M.fromList [(1, "x")]))
print (listToMaybe [9, 8], listToMaybe ([] :: [Int]))
case sicherDiv 20 4 of
Nothing -> putStrLn "Fehler"
Just n -> putStrLn ("Ergebnis " ++ show n)
print (fmap (+ 1) (Just 5), fmap (+ 1) Nothing)
print ((+) <$> Just 3 <*> Just 4, (+) <$> Just 3 <*> Nothing)
print (Just 5 >>= \x -> sicherDiv 100 x >>= \y -> Just (y + 1))
print (isJust (Just 1), (<|>) Nothing (Just 2))
where (<|>) a b = maybe b Just aAusgabe:
(Just 5,Nothing)
0
"3"
(Just 42,Nothing,Just 3.5)
[1,3]
[1,3]
(Just 2,Nothing)
(Just 9,Nothing)
Ergebnis 5
(Just 6,Nothing)
(Just 7,Nothing)
Just 21
(True,Just 2)Either: Erfolg oder Fehler mit Grund
Either e a: Left fehler oder Right wert. Konvention: Right ist “richtig”.
import Data.Char (isDigit)
data Fehler = Leer | KeineZahl String | ZuGross Int deriving (Show, Eq)
parseAlter :: String -> Either Fehler Int
parseAlter "" = Left Leer
parseAlter s
| not (all isDigit s) = Left (KeineZahl s)
| n > 150 = Left (ZuGross n)
| otherwise = Right n
where n = read s
pruefe :: Int -> Either Fehler String
pruefe a = Right (if a >= 18 then "volljährig" else "minderjährig")
main :: IO ()
main = do
print (map parseAlter ["17", "", "abc", "200"])
print (parseAlter "30" >>= pruefe)
print (parseAlter "x" >>= pruefe)
print (fmap (* 2) (parseAlter "21"))
print (either show (\a -> "Alter " ++ show a) (parseAlter "12"))
print (traverse parseAlter ["1", "2", "3"])
print (traverse parseAlter ["1", "x", "3"])
let ergebnis = do -- do-Notation für Either
a <- parseAlter "40"
b <- parseAlter "2"
Right (a + b)
print ergebnisAusgabe:
[Right 17,Left Leer,Left (KeineZahl "abc"),Left (ZuGross 200)]
Right "vollj\228hrig"
Left (KeineZahl "x")
Right 42
"Alter 12"
Right [1,2,3]
Left (KeineZahl "x")
Right 42Das Ergebnis Left bricht die Kette ab (wie eine Exception), ohne Ausnahmen zu werfen: Fehler sind Werte im Typ.
Functor, Applicative, Monad
Alle bisherigen Typen (Maybe, Either, Listen, IO) gehören zu denselben Typklassen:
| Klasse | Funktion | Idee |
|---|---|---|
Functor | fmap f x bzw. f <$> x | Funktion in den Container anwenden |
Applicative | pure x, f <*> x | Funktionen im Container anwenden, kombinieren |
Monad | x >>= f | Abläufe verketten, bei denen jeder Schritt vom vorigen abhängt |
main :: IO ()
main = do
print (fmap (* 2) [1, 2, 3], (+ 1) <$> Just 1, fmap length (Right "abc" :: Either Int String))
print (pure 5 :: [Int], pure 5 :: Maybe Int)
print ((+) <$> [1, 2] <*> [10, 20]) -- alle Kombinationen
print ((,) <$> Just 'a' <*> Just True)
print ([1, 2, 3] >>= \x -> [x, x * 10])
print (do { x <- [1, 2]; y <- "ab"; return (x, y) })
print (sequence [Just 1, Just 2], sequence [Just 1, Nothing])
print (mapM (\x -> if x > 0 then Just x else Nothing) [1, 2, 3])
print (zipWith3 (\a b c -> a + b + c) [1, 2] [10, 20] [100, 200])
print (fmap (+ 1) (3, 4), fst <$> Just (1, 2))
print (concat <$> mapM (\x -> Just [x, x]) [1, 2])Ausgabe:
([2,4,6],Just 2,Right 3)
([5],Just 5)
[11,21,12,22]
Just ('a',True)
[1,10,2,20,3,30]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
(Just [1,2],Nothing)
Just [1,2,3]
[111,222]
((3,5),Just 1)
Just [1,1,2,2]Merke
Maybe a(Just/Nothing) statt null,Either e a(Left/Right) für Fehler mit GrundfromMaybe,maybe,either,readMaybe,mapMaybefmap/<$>,<*>,>>=: Funktionen über Container und Abläufedo-Notation verkettet Maybe/Either/Listen/IO eleganttraverse/mapM/sequencedrehen “Liste von Maybe” zu “Maybe von Liste”
Übungsaufgabe
Schreibe sicherWurzel :: Double -> Maybe Double und verkette zwei Aufrufe mit >>=.
Quiz zur Selbstkontrolle
Wofür steht Nothing?
- Kein Wert (Gegenstück zu Just) (richtig)
- Null
- Eine leere Liste
- Ein Fehler zur Laufzeit
Was liefert fmap (+1) (Just 2)?
- Just 3 (richtig)
- 3
- Nothing
- [3]
Wofür nutzt man Either?
- Für Ergebnisse, die Erfolg oder einen Fehlergrund sein können (richtig)
- Für Schleifen
- Für Zufall
- Für Dateien
Weiter im Kurs
Zurück: Eigene Typen und Typklassen
Weiter: IO, do-Notation und Zustand
Alle Kapitel: Haskell im Überblick