tite fractale

Comment Haskell gère les entrées/sorties

Lancer le diaporama

Webographie :

1. Comment Haskell gère l’IO


2. Le cadre


3. Un premier programme

Une sortie

main :: IO ()
main = affiche "Hello World!"

affiche :: String -> IO ()
affiche = putStrLn

4. Un deuxième programme

Une entrée puis une sortie, première version.

main :: IO ()
main = do
    input <- demander
    affiche (a_lenvers input)

demander :: IO String
demander = getLine

affiche :: String -> IO ()
affiche = putStrLn

-- a_lenvers est pure (non marquée IO)
a_lenvers :: [a] -> [a]
a_lenvers = reverse

5. Un deuxième programme

Une entrée puis une sortie, deuxième version.

main :: IO ()
main = do
    reversed_input <- (fmapIO a_lenvers demander)
    affiche reversed_input

fmapIO :: (a -> b) -> (IO a) -> (IO b)
fmapIO = fmap                         -- IO est un foncteur

demander :: IO String
demander = getLine

affiche :: String -> IO ()
affiche = putStrLn

a_lenvers :: String -> String
a_lenvers = reverse

6. Un deuxième programme

Une entrée puis une sortie, troisième version.

import Control.Applicative

main :: IO ()          -- IO est aussi un foncteur applicatif
main = do
    reversed_input <- ((pure a_lenvers) <*> demander)
--  reversed_input <- (a_lenvers <$> demander)
    affiche reversed_input

demander :: IO String
demander = getLine

affiche :: String -> IO ()
affiche = putStrLn

a_lenvers :: String -> String
a_lenvers = reverse

Note : ($) :: (a -> b) -> a -> b est l’opérateur d’application. f $ x signifie f x (détails plus loin).


7. Un deuxième programme

Une entrée puis une sortie, quatrième version.

main :: IO ()
main = interaction a_lenvers

interaction :: (String -> String) -> IO ()
interaction = interact          -- très pratique pour stdin -> stdout

a_lenvers :: String -> String
a_lenvers = reverse

Utilisation :


8. Un deuxième programme

Une entrée puis une sortie, quelques variations en point-free.

main = interact reverse

 

main = interact unwords.(map (++ " !!! ")).words.(map toUpper)

9. Monades


10. Foncteurs, généralités

class Functor f where
    fmap :: (a -> b) -> f a -> f b

11. Foncteurs, []

fmap :: (a -> b) -> f a -> f b nous rappelle map :: (a -> b) -> [a] -> [b].

En fait on a même :

instance Functor [] where
    fmap = map

Une liste est une boîte contenant une ou plusieurs valeurs, voire aucune.

Plus précisément, une liste peut être vue comme une valeur non déterministe, pouvant prendre aucune, une, ou plusieurs valeurs.


12. Foncteurs, Maybe

Rappel : data Maybe a = Nothing | Just a

instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing = Nothing

On a ici une boîte contenant une valeur ou ne contenant rien.

Plus précisément, le calcul est dans un contexte de réussite.
Il peut aboutir ou non, être valide ou non…


13. Foncteurs, autres


14. Foncteurs, (->) r

instance Functor ((->) r) where
    fmap f g = (\x -> f (g x))

Le type de fmap devient ici : fmap :: (a -> b) -> ((->) r a) -> ((->) r b) c’est-à-dire en infixe : fmap :: (a -> b) -> (r -> a) -> (r -> b).

instance Functor ((->) r) where
    fmap = (.)

Les valeurs cachées dans la boîte sont les images de la fonction.


15. Foncteurs, IO

Fait appel à >>= et return, que nous détaillerons bientôt.

instance Functor IO where
    fmap f ioa = ioa >>= (\a -> return (f a))
 -- fmap f ioa = ioa >>= (return . f)

Voire, avec do :

instance Functor IO where
    fmap f ioa = do
        value <- ioa
        return (f value)

Le contexte de calcul est une entrée ou une sortie (ou les deux).

On récupère la valeur de l’action, on lui applique f, puis on remet le résultat dans un contexte d’entrée/sortie (destruction, application, construction).


16. Foncteurs, généralités

-> étant associative à droite, on a aussi :

fmap :: (a -> b) -> (f a -> f b)

c’est-à-dire que si on ne donne à fmap qu’un paramètre, on obtient une fonction qui « travaille dans le foncteur ». On dit qu’on lifte la fonction, qu’on l’élève.

Un exemple de la fonction double, liftée dans les listes :

ghci> let f = (* 2)
ghci> let f' = fmap f :: [Integer] -> [Integer]
ghci> f' [0..5]
[0,2,4,6,8,10]

17. Foncteurs, généralités

Lois des foncteurs, non imposées en Haskell :

fmap id = id
fmap (f . g) = fmap f . fmap g

18. Foncteurs applicatifs, généralités

class (Functor f) => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

19. Foncteurs applicatifs, généralités

Rappel :

($) :: (a -> b) -> a -> b

20. Foncteurs applicatifs, Maybe

instance Applicative Maybe where
    pure = Just
    Nothing <*> _ = Nothing
    (Just f) <*> something = fmap f something

21. Foncteurs applicatifs, []

instance Applicative [] where
    pure x = [x]
    fs <*> xs = [f x | f <- fs, x <- xs]

22. Foncteurs applicatifs, (->) r

instance Applicative ((->) r) where
    pure x = (\_ -> x)
    f <*> g = \x -> f x (g x)

23. Foncteurs applicatifs, IO

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)

24. Foncteurs applicatifs, généralités

-- lift d’une fonction à deux paramètres
liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b
-- rassembler plusieurs contextes en un
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
-- idem avec un fold
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA = foldr (liftA2 (:)) (pure [])

25. Foncteurs applicatifs, généralités

ghci> sequenceA [Just 3, Just 2, Just 1]
Just [3,2,1]
ghci> sequenceA [Just 3, Nothing, Just 1]
Nothing
ghci> sequenceA [[1,2,3],[4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]
[]
ghci> sequenceA [(+3),(+2),(+1)] 3
[6,5,4]

26. Foncteurs applicatifs, généralités

Dans le foncteur IO, sequenceA est sequence.

ghci> :t sequence
sequence :: Monad m => [m a] -> m [a]
ghci> sequence [getLine, getLine, getLine, getLine]
bonjour
tout
le
monde
["bonjour","tout","le","monde"]

27. Foncteurs applicatifs, lois

pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u

28. Monades, généralités


29. Monades, généralités

class Monad m where
    return :: a -> m a

    -- bind
    (>>=) :: m a -> (a -> m b) -> m b

    -- then
    (>>) :: m a -> m b -> m b
    x >> y = x >>= \_ -> y

    fail :: String -> m a
    fail msg = error msg

30. Monades, Maybe

instance Monad Maybe where
    return x = Just x
    Nothing >>= f = Nothing
    Just x >>= f = f x
    fail _ = Nothing

31. Monades, Maybe

Un exemple :

ghci> let f n = if n==0 then Nothing else Just (n-1)
ghci> f 18
Just 17
ghci> f 1
Just 0
ghci> f 0
Nothing
ghci> (Just 2) >>= f >>= f
Just 0
ghci> (Just 2) >>= f >>= f >>= f
Nothing

32. Monades, Maybe

Un exemple illustrant la notation do :

ghci> let positive_sum x y = if x+y > 0 then Just (x+y) else Nothing
ghci> :t positive_sum
positive_sum :: (Ord a, Num a) => a -> a -> Maybe a
ghci> :t positive_sum 0
positive_sum 0 :: (Ord a, Num a) => a -> Maybe a
ghci> :t \y -> positive_sum 0 y
\y -> positive_sum 0 y :: (Ord a, Num a) => a -> Maybe a

ghci> positive_sum 1 2
Just 3
ghci> positive_sum 1 (-2)
Nothing

33. Monades, Maybe

Un exemple illustrant la notation do :

ghci> Just 1 >>= (\x -> positive_sum x 2)
Just 3
ghci> Just 1 >>= (\x -> (Just 2 >>= (\y -> positive_sum x y)))
Just 3
ghci> do x <- Just 1; y <- Just 2; positive_sum x y
Just 3
ghci> do x <- Just 1; y <- Just (-2); positive_sum x y
Nothing

La notation do (dont fait partie <-) n’est que du sucre syntactique pour >>=.


34. Monades, []

instance Monad [] where
    return x = [x]
    xs >>= f = concat (map f xs)
    fail _ = []

35. Monades, []

Un exemple :

ghci> let plus2 x = [x+2]
ghci> :t plus2
plus2 :: Num t => t -> [t]
ghci> [1, 2] >>= plus2
[3,4]
ghci> [1, 2] >>= plus2 >>= plus2
[5,6]
ghci> let plus1_plus2 x = [x+1, x+2]
ghci> [1, 2] >>= plus1_plus2
[2,3,3,4]
ghci> [1, 2] >>= plus1_plus2 >>= plus1_plus2
[3,4,4,5,4,5,5,6]

36. Monades, []

Les listes en compréhension ne sont que du sucre syntactique autour de la notion de MonadPlus (combinaison de monade et de monoïde).

class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a

instance MonadPlus [] where
    mzero = []
    mplus = (++)

guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero

-- lois
mzero `mplus` m  =  m
m `mplus` mzero  =  m
m `mplus` (n `mplus` o)  =  (m `mplus` n) `mplus` o
...

37. Monades, []

multiplyTo :: Int -> [(Int, Int)]
multiplyTo n = do
  x <- [1..n]
  y <- [x..n]
  guarded (x * y == n) $ return (x, y)

guarded :: Bool –> [ a ] –> [ a ]
guarded True  xs = xs
guarded False _  = []

Voir l’article.


38. Monades, ((->) r)

-- Control.Monad.Instances
instance Monad ((->) r) where
    return x = \_ -> x
    h >>= f = \w -> f (h w) w

C’est la monade Reader, qui injecte la même valeur dans toutes les fonctions qu’elle assemble.


39. Monades, IO

IO ne peut pas être implémentée en Haskell pur.


40. Monades, retour sur la notation do

main :: IO ()
main = demander >>= (\input -> affiche (a_lenvers input))

 

main :: IO ()
main = demander >>= \input ->
       affiche (a_lenvers input)

 

main :: IO ()
main = do
    input <- demander
    affiche (a_lenvers input)

41. Monades, retour sur la notation do

main = demander >>= (\input1 ->
       demander >>= (\input2 ->
       affiche (input2 ++ input1)))

 

main = demander >>= \input1 ->
       demander >>= \input2 ->
       affiche (input2 ++ input1)

 

main = do
    input1 <- demander
    input2 <- demander
    affiche (input2 ++ input1)

42. Monades, retour sur la notation do


43. Monade IO, isoler le code impur


44. Monades, lois

return a >>= f = f a
m >>= return = m
(m >>= f) >>= g = m >>= (\x -> f x >>= g)

45. Monades, lois

-- Avec cet opérateur de composition:
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
(m >=> n) x = do
    y <- m x
    n y

return >=> g = g
f >=> return = f
(f >=> g) >=> h = f >=> (g >=> h)

46. Monades, lifting

liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = m >>= (\x -> return (f x))

liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
    x <- m
    return (f x)

47. Monades, autres fonctions

On peut citer join, mapM, filterM, foldM, entre autres.

join :: Monad m => m (m a) -> m a
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
foldM_ :: Monad m => (a -> b -> m a) -> a -> [b] -> m ()

Voir cette page.


48. Autres monades


49. Monades, bilan







Fin




Christophe Gragnic, qui remercie Miran Lipovača,, le 26/09/2014, 15h05'58".






Page générée le 27/05/2021, 09h06'59" (source).
historique de la page
historique global