Éclaircissements sur les monades

Bonjour, Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative). Merci par avance pour vos réponses.

Qu’est ce qui est un peu flou dans la notion de contexte ? A part le fait que ce soit justement un mot flou… qui peut désigner n’importe quoi (une liste, une valeur optionnelle, le monde).
Sur le fait que les monade soient plus aptes à contenir les effets de bord, il me semble que c’est parce que le concept d’effet de bord implique la séquentialité, un avant et un après : l’ordre dans lequel les effets de bord opèrent est important. Et c’est justement la propriété essentielle des monades qui n’est pas celle des foncteurs applicatifs que de séquencer des “opérations”.
Je trouve l’article de Wikipedia très clair : https://en.wikipedia.org/wiki/Monads_in_functional_programming
Arnaud
On 26 Mar 2014, at 18:10, Gautier DI FOLCO
Bonjour,
Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative).
Merci par avance pour vos réponses. _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 26 mars 2014 17:31, Arnaud Bailly
Qu'est ce qui est un peu flou dans la notion de contexte ? A part le fait que ce soit justement un mot flou... qui peut désigner n'importe quoi (une liste, une valeur optionnelle, le monde).
Par exemple que représente le fait d'avoir comme contexte/cadre de calcul une liste ? que je ne peux utiliser que les fonctions propres aux listes ?
Sur le fait que les monade soient plus aptes à contenir les effets de bord, il me semble que c'est parce que le concept d'effet de bord implique la séquentialité, un avant et un après : l'ordre dans lequel les effets de bord opèrent est important. Et c'est justement la propriété essentielle des monades qui n'est pas celle des foncteurs applicatifs que de séquencer des "opérations".
Je trouve l'article de Wikipedia très clair : https://en.wikipedia.org/wiki/Monads_in_functional_programming
Arnaud
Ma question est là, qu'est-ce qui défini cette notion de séquence ? en quoi les 4-5 fonctions du typeclass font qu'il y a une notion de séquence ?

Que tu effectue un calcul qui produit plusieurs résultats. Mois ça m’aide de comprendre les monades vraiment comme des objets que tu construis.
On 27 Mar 2014, at 00:47, Gautier DI FOLCO
Le 26 mars 2014 17:31, Arnaud Bailly
a écrit : Qu’est ce qui est un peu flou dans la notion de contexte ? A part le fait que ce soit justement un mot flou… qui peut désigner n’importe quoi (une liste, une valeur optionnelle, le monde). Par exemple que représente le fait d'avoir comme contexte/cadre de calcul une liste ? que je ne peux utiliser que les fonctions propres aux listes ?
Sur le fait que les monade soient plus aptes à contenir les effets de bord, il me semble que c’est parce que le concept d’effet de bord implique la séquentialité, un avant et un après : l’ordre dans lequel les effets de bord opèrent est important. Et c’est justement la propriété essentielle des monades qui n’est pas celle des foncteurs applicatifs que de séquencer des “opérations”.
Je trouve l’article de Wikipedia très clair : https://en.wikipedia.org/wiki/Monads_in_functional_programming
Arnaud
Ma question est là, qu'est-ce qui défini cette notion de séquence ? en quoi les 4-5 fonctions du typeclass font qu'il y a une notion de séquence ? _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le truc du contexte, je pense qu'ya une bonne image pour le représenter.
Ce qui est propre au monade et que n'ont pas les applicatives, c'est que la
suite d'une fonction dépende de ce qui se passe à un moment donné.
Exemple:
foo :: IO ()
foo = do
s <- getLine
case s of
"hello" -> putStrLn " world!"
_ -> foo
ici, on fait quelque chose dans IO, et selon le résultat, il peut se passer
deux choses complètement différentes. On se sert beucoup de ce genre de
"contexte" dans les parsers par exemple, c'est littéralement explicit, les
parsers ont une forte analogie avec la monade State.
Alors qu'avec Applicative, tu peux avoir des "effets", des computations
avec un petit truc en plus (parseurs qui retournent une valeur mais qui
entre temps ont consumé une partie de l'input pour produire ces valeurs),
etc, mais t'es obligé de garder la même "structure" pour ton code, ex:
data Person = Person { age :: Int, name :: String }
blah :: Parser Person
blah = Parser
<$> parseInt
<*> parseString
T'auras beau faire ce que tu veux, taper dans tes mains, danser la salsa,
etc, tu pourras pas changer le comportement de ce parseur de "personnes"
selon la valeur de l'Int ou de la chaine de caractères.
Enfin, pour terminer, c'est cet article que j'ai trouvé le plus profond sur
les monades:
http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html
En espérant que ça t'aide à comprendre!
2014-03-26 17:47 GMT+01:00 Gautier DI FOLCO
Le 26 mars 2014 17:31, Arnaud Bailly
a écrit : Qu’est ce qui est un peu flou dans la notion de contexte ? A part le fait
que ce soit justement un mot flou… qui peut désigner n’importe quoi (une liste, une valeur optionnelle, le monde).
Par exemple que représente le fait d'avoir comme contexte/cadre de calcul une liste ? que je ne peux utiliser que les fonctions propres aux listes ?
Sur le fait que les monade soient plus aptes à contenir les effets de bord, il me semble que c’est parce que le concept d’effet de bord implique la séquentialité, un avant et un après : l’ordre dans lequel les effets de bord opèrent est important. Et c’est justement la propriété essentielle des monades qui n’est pas celle des foncteurs applicatifs que de séquencer des “opérations”.
Je trouve l’article de Wikipedia très clair : https://en.wikipedia.org/wiki/Monads_in_functional_programming
Arnaud
Ma question est là, qu'est-ce qui défini cette notion de séquence ? en quoi les 4-5 fonctions du typeclass font qu'il y a une notion de séquence ?
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
-- Alp Mestanogullari

Le 26 mars 2014 17:55, Alp Mestanogullari
Le truc du contexte, je pense qu'ya une bonne image pour le représenter.
Ce qui est propre au monade et que n'ont pas les applicatives, c'est que la suite d'une fonction dépende de ce qui se passe à un moment donné. Exemple:
foo :: IO () foo = do s <- getLine case s of "hello" -> putStrLn " world!" _ -> foo
ici, on fait quelque chose dans IO, et selon le résultat, il peut se passer deux choses complètement différentes. On se sert beucoup de ce genre de "contexte" dans les parsers par exemple, c'est littéralement explicit, les parsers ont une forte analogie avec la monade State.
En gros tu crée une dépendance qui force ton exécution dans un certain ordre en lui passant le précédent monde ?
Alors qu'avec Applicative, tu peux avoir des "effets", des computations avec un petit truc en plus (parseurs qui retournent une valeur mais qui entre temps ont consumé une partie de l'input pour produire ces valeurs), etc, mais t'es obligé de garder la même "structure" pour ton code, ex:
Tu entends quoi par "computation" (désolé, je bloque sur ce terme, ça ne rentre pas :/) Tu veux dire truc en moins ou Monade ? si non, je sur largué. Je ne comprends pas la dernière partie de la phrase.
data Person = Person { age :: Int, name :: String }
blah :: Parser Person blah = Parser <$> parseInt <*> parseString
T'auras beau faire ce que tu veux, taper dans tes mains, danser la salsa, etc, tu pourras pas changer le comportement de ce parseur de "personnes" selon la valeur de l'Int ou de la chaine de caractères.
Perdu :/ Quelle est la "déclaration" (je ne sais pas comment ça se dit : data/type/newtype) de Parser ? Comment le monde est "passé" aux parse* ? Comment il sait quel type de Parser il doit produire ? (le première ligne du corps de blah appel le Constructeur du type Parser, comment sait-il qu'il va "retourner" une valeur de type Parser Person ? vu que Person n'est indiqué nul part dans le corps).
Enfin, pour terminer, c'est cet article que j'ai trouvé le plus profond sur les monades: http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html
Je vais le rererelire, à chaque fois je comprends un truc de nouveau.

Ok, je vais essayer de reprendre plus tranquillou ce que j'ai dit en
répondant point par point.
2014-03-26 19:32 GMT+01:00 Gautier DI FOLCO
Le 26 mars 2014 17:55, Alp Mestanogullari
a écrit : Le truc du contexte, je pense qu'ya une bonne image pour le représenter.
Ce qui est propre au monade et que n'ont pas les applicatives, c'est que la suite d'une fonction dépende de ce qui se passe à un moment donné. Exemple:
foo :: IO () foo = do s <- getLine case s of "hello" -> putStrLn " world!" _ -> foo
ici, on fait quelque chose dans IO, et selon le résultat, il peut se passer deux choses complètement différentes. On se sert beucoup de ce genre de "contexte" dans les parsers par exemple, c'est littéralement explicit, les parsers ont une forte analogie avec la monade State.
En gros tu crée une dépendance qui force ton exécution dans un certain ordre en lui passant le précédent monde ?
Bah, tu as tout simplement le moyen de faire ça avec ce que te donne IO. Mais tu pourrais faire pareil dans une autre monade. L'idée c'est que là, 'getLine' c'est de type IO String, donc 's' est une String, on est d'accord? Hé bien on peut grâce à la do-notation (c'est grâce à la façon dont la do-notation est traduite en avalanche de >>= et return & compagnie en fait). Comment? Très simple: f :: IO () f = do s <- getLine putStrLn s se traduit en f' :: IO f' = getLine >>= \s -> putStrLn s -- ou plus simple: f' = getLine >>= putStrLn Re-regardons le type de >>= pour IO: (>>=) :: IO a -> (a -> IO b) -> IO b En gros t'as qqch qui va te produire un 'a' à travers des effets (lecture de fichiers, depuis une DB, depuis stdin, ...) - (c'est ça la signification de 'IO a') - et tu vois que le deuxième argument de >>= prend un 'a', donc en gros >>= donne à son deuxième argument la valeur générée par le premier argument. C'est cette "inspection" qui te permet en gros de faire un peu ce que tu veux avec ce 'a' tant que tu produis un 'IO b' pour un certain type 'b'.
Alors qu'avec Applicative, tu peux avoir des "effets", des computations
avec un petit truc en plus (parseurs qui retournent une valeur mais qui entre temps ont consumé une partie de l'input pour produire ces valeurs), etc, mais t'es obligé de garder la même "structure" pour ton code, ex:
Tu entends quoi par "computation" (désolé, je bloque sur ce terme, ça ne rentre pas :/)
l'évaluation d'une expression, de qqch.
Tu veux dire truc en moins ou Monade ? si non, je sur largué.
Non non, je veux dire que Monad permet ça aussi, mais Applicative ne permet *QUE* ça, alors que Monad, comme montré juste au-dessus, permet d'inspecter ce que t'as produit et réagir en conséquence. Applicative te permet juste d'appliquer des fonctions pures, par ex de type 'a -> b -> c', à des arguments qui viennent "d'ailleurs", par ex 'IO a' et 'IO b'. Exemple: data ProgrammingLanguage = ProgrammingLanguage { name :: String, fileExtension :: String } getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = ProgrammingLanguage <$> getLine <*> getLine Ca va te permettre d'entrer le nom d'un langage, <ENTER>, l'extension des fichiers associée à ce langage (".hs" pour Haskell par exemple, quoi), et ça te retournera une valeur de type ProgrammingLanguage. Si tu utilisais les fonctions de Monad pour faire ça: getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = do name <- getLine fileExt <- getLine return $ ProgrammingLanguage name fileExt Tu noteras que contrairement à mon exemple à la con de 'case s of "hello" -> ...', ici on cherche pas à agir différemment selon la valeur qu'on obtient grâce à getLine, que ce soit pour le nom où l'extension, et c'est exactement ce qui fait qu'on peut écrire cette fonction tout à fait utile aussi bien avec les fonctions d'Applicative ( <*> combinée à <$> = fmap de Functor ) qu'avec la do-notation. Histoire d'être complet, si tu veux écrire ça sans le do mais quand même avec les fonctions de Monad: getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = getLine >>= \name -> getLine >>= \fileExt -> return (ProgrammingLanguage name fileExt)
Perdu :/ Quelle est la "déclaration" (je ne sais pas comment ça se dit : data/type/newtype) de Parser ?
Peu importe, mais il est établi que les parseurs sont Monad/Applicative. "Parser Int" c'est qqch qui va essayer de parser un int quand tu lui donneras une String sur laquelle essayer. Parser Person c'est un truc qui va essayer de parser une "Person" quand tu lui donneras une entrée sur laquelle essayer. Comment? Il va essayer de parser un Int, puis une String, et si les deux réussissent, il passe tout ça au constructeur 'Person'. Comment le monde est "passé" aux parse* ?
Ca fait partie du fonctionnement interne. Généralement un parseur c'est une fonction du genre 'String -> (a, String)'. Donc un truc qui attend une String en entrée et qui va tenter de produire un 'a' (Person par exemple dans notre exemple) grâce à ce qu'il trouvera dans cette String et retournera à côté de la valeur "ce qui n'a pas encore été consommé". Dans la vraie vie, ils sont plus compliqués, mais ça te donne peut-être une idée.
Comment il sait quel type de Parser il doit produire ? (le première ligne du corps de blah appel le Constructeur du type Parser, comment sait-il qu'il va "retourner" une valeur de type Parser Person ? vu que Person n'est indiqué nul part dans le corps).
Il le sait parce que il connait le type des attributs de Person (age de type Int, name de type String) et il connait le type de parseInt et parseString. Et si, justement, Person est indiqué dans le corps. La version Monad t'aidera peut-être: parsePerson :: Parser Person parsePerson = do age <- parseInt name <- parseString return (Person age name) La version applicative fait *EXACTEMENT* ça. Ne t'inquiètes pas, on est tous passés par là, faut changer sa façon de penser pour bien piger tout ça. Faut oublier toutes les merdes qu'on a appris ailleurs, repenser la programmation. Ca va prendre un peu de temps mais tu vas y arriver. En attendant, hésite pas à signaler ce qui n'est toujours pas clair après ce message. -- Alp Mestanogullari

Le 26 mars 2014 19:58, Alp Mestanogullari
Ok, je vais essayer de reprendre plus tranquillou ce que j'ai dit en répondant point par point.
2014-03-26 19:32 GMT+01:00 Gautier DI FOLCO
: Le 26 mars 2014 17:55, Alp Mestanogullari
a écrit : Le truc du contexte, je pense qu'ya une bonne image pour le représenter.
Ce qui est propre au monade et que n'ont pas les applicatives, c'est que la suite d'une fonction dépende de ce qui se passe à un moment donné. Exemple:
foo :: IO () foo = do s <- getLine case s of "hello" -> putStrLn " world!" _ -> foo
ici, on fait quelque chose dans IO, et selon le résultat, il peut se passer deux choses complètement différentes. On se sert beucoup de ce genre de "contexte" dans les parsers par exemple, c'est littéralement explicit, les parsers ont une forte analogie avec la monade State.
En gros tu crée une dépendance qui force ton exécution dans un certain ordre en lui passant le précédent monde ?
Bah, tu as tout simplement le moyen de faire ça avec ce que te donne IO. Mais tu pourrais faire pareil dans une autre monade. L'idée c'est que là, 'getLine' c'est de type IO String, donc 's' est une String, on est d'accord? Hé bien on peut grâce à la do-notation (c'est grâce à la façon dont la do-notation est traduite en avalanche de >>= et return & compagnie en fait). Comment?
Très simple:
f :: IO () f = do s <- getLine putStrLn s
se traduit en
f' :: IO f' = getLine >>= \s -> putStrLn s
-- ou plus simple: f' = getLine >>= putStrLn
Je viens de piger je crois. En fait je me mélangeais dans le sésucrage, c'est plus claire déjà.
Re-regardons le type de >>= pour IO:
(>>=) :: IO a -> (a -> IO b) -> IO b
En gros t'as qqch qui va te produire un 'a' à travers des effets (lecture de fichiers, depuis une DB, depuis stdin, ...) - (c'est ça la signification de 'IO a') - et tu vois que le deuxième argument de >>= prend un 'a', donc en gros >>= donne à son deuxième argument la valeur générée par le premier argument. C'est cette "inspection" qui te permet en gros de faire un peu ce que tu veux avec ce 'a' tant que tu produis un 'IO b' pour un certain type 'b'.
ça c'était bon (je suis fier de moi alors que c'est "tout bête")
Alors qu'avec Applicative, tu peux avoir des "effets", des computations
avec un petit truc en plus (parseurs qui retournent une valeur mais qui entre temps ont consumé une partie de l'input pour produire ces valeurs), etc, mais t'es obligé de garder la même "structure" pour ton code, ex:
Tu entends quoi par "computation" (désolé, je bloque sur ce terme, ça ne rentre pas :/)
l'évaluation d'une expression, de qqch.
pas mieux, ça correspond à quoi ? Tu veux dire truc en moins ou Monade ? si non, je sur largué.
Non non, je veux dire que Monad permet ça aussi, mais Applicative ne permet *QUE* ça, alors que Monad, comme montré juste au-dessus, permet d'inspecter ce que t'as produit et réagir en conséquence. Applicative te permet juste d'appliquer des fonctions pures, par ex de type 'a -> b -> c', à des arguments qui viennent "d'ailleurs", par ex 'IO a' et 'IO b'.
Exemple:
data ProgrammingLanguage = ProgrammingLanguage { name :: String, fileExtension :: String }
getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = ProgrammingLanguage <$> getLine <*> getLine
Ca va te permettre d'entrer le nom d'un langage, <ENTER>, l'extension des fichiers associée à ce langage (".hs" pour Haskell par exemple, quoi), et ça te retournera une valeur de type ProgrammingLanguage.
Si tu utilisais les fonctions de Monad pour faire ça:
getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = do name <- getLine fileExt <- getLine return $ ProgrammingLanguage name fileExt
Tu noteras que contrairement à mon exemple à la con de 'case s of "hello" -> ...', ici on cherche pas à agir différemment selon la valeur qu'on obtient grâce à getLine, que ce soit pour le nom où l'extension, et c'est exactement ce qui fait qu'on peut écrire cette fonction tout à fait utile aussi bien avec les fonctions d'Applicative ( <*> combinée à <$> = fmap de Functor ) qu'avec la do-notation.
ok pigé, je ne voyais pas où tu voulais en venir avant (je pensais que tu parlais de faire des IO via des Applicatives).
Histoire d'être complet, si tu veux écrire ça sans le do mais quand même avec les fonctions de Monad:
getProgrammingLanguageFromStdin :: IO ProgrammingLanguage getProgrammingLanguageFromStdin = getLine >>= \name -> getLine >>= \fileExt -> return (ProgrammingLanguage name fileExt)
Perdu :/ Quelle est la "déclaration" (je ne sais pas comment ça se dit : data/type/newtype) de Parser ?
Peu importe, mais il est établi que les parseurs sont Monad/Applicative. "Parser Int" c'est qqch qui va essayer de parser un int quand tu lui donneras une String sur laquelle essayer.
Parser Person c'est un truc qui va essayer de parser une "Person" quand tu lui donneras une entrée sur laquelle essayer. Comment? Il va essayer de parser un Int, puis une String, et si les deux réussissent, il passe tout ça au constructeur 'Person'.
Comment le monde est "passé" aux parse* ?
Ca fait partie du fonctionnement interne. Généralement un parseur c'est une fonction du genre 'String -> (a, String)'. Donc un truc qui attend une String en entrée et qui va tenter de produire un 'a' (Person par exemple dans notre exemple) grâce à ce qu'il trouvera dans cette String et retournera à côté de la valeur "ce qui n'a pas encore été consommé". Dans la vraie vie, ils sont plus compliqués, mais ça te donne peut-être une idée.
ok, compris.
Comment il sait quel type de Parser il doit produire ? (le première
ligne du corps de blah appel le Constructeur du type Parser, comment sait-il qu'il va "retourner" une valeur de type Parser Person ? vu que Person n'est indiqué nul part dans le corps).
Il le sait parce que il connait le type des attributs de Person (age de type Int, name de type String) et il connait le type de parseInt et parseString. Et si, justement, Person est indiqué dans le corps. La version Monad t'aidera peut-être:
parsePerson :: Parser Person parsePerson = do age <- parseInt name <- parseString return (Person age name)
La version applicative fait *EXACTEMENT* ça.
ok, je comprends.
Ne t'inquiètes pas, on est tous passés par là, faut changer sa façon de penser pour bien piger tout ça. Faut oublier toutes les merdes qu'on a appris ailleurs, repenser la programmation. Ca va prendre un peu de temps mais tu vas y arriver.
En attendant, hésite pas à signaler ce qui n'est toujours pas clair après ce message.
Merci, merci encore, c'est ça que j'aime bien en Haskell, dès que tu as compris c'est limpide mais tu as toujours des choses à apprendre.

Bonjour,
J'avais fait un exposé pour expliquer ça entre autres, je ne sais pas si
c'est tellement compréhensible sans les explications données à l'oral mais
les slides sont là :
http://www.sylvain-henry.info/home/data/uploads/talks/shenry-2013-02-05-haskell-intro.pdf(à
partir du 20e slide).
* Contexte
Dans un langage fonctionnel, on n'utilise pas de variable globale donc pour
qu'il y ait un contexte il faut le passer explicitement en entrée des
fonctions et le renvoyer en sortie (en plus d'autres valeurs
potentiellement) :
f :: ... -> Context -> (Context, ...)
pour ne pas écrire à chaque fois les types "Context", on peut faire :
type M a = Context -> (Context, a)
et réécrire :
f :: ... -> M a
Il ne reste plus qu'à définir la composition pour les fonctions de ce type
de sorte que le contexte soit passé de la sortie de la première fonction à
l'entrée de l'autre :
(>>=) :: M a -> (a -> M b) -> M b
(>>=) f g = \ctx -> let (ctx2, a) = f ctx in g a ctx2
Pour créer une fonction de type "M a" à partir d'un "a", on définit la
fonction :
return :: a -> M a
return x = \ctx -> (ctx,x)
* IO
Dans le cas des IO, le "contexte" est le "monde" (extérieur au programme).
La fonction main a le type suivant :
main :: IO ()
soit
main :: World -> (World, () )
Donc on peut composer des fonctions qui ont des effets de bord avec (>>=)
de sorte que ce soit correct d'un point de vue types sans jamais dire
exactement ce que représente le type World. Les opérations s'exécutent en
séquence puisque chaque opération "attend de récupérer le monde renvoyé par
l'opération qui la précède" (du moins c'est l'idée). Il n'y a aucun moyen
pour le programme de créer un objet de type World donc toutes les
opérations s'enchainent forcément à partir de celui passé à "main".
* Monades
Les monades sont juste une généralisation du mécanisme décrit ci-dessus (en
gros), c'est pourquoi ils sont adaptés. La typeclass Monad implique la
typeclass Applicative (l'inverse n'est pas vrai) et on aurait pu utiliser
Applicative à la place de Monad dans le cas des IOs. Ça a même été suggéré
sur la mailing-list haskell-cafe il n'y a pas si longtemps si je me
souviens bien.
@+
Sylvain
Le 26 mars 2014 11:10, Gautier DI FOLCO
Bonjour,
Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative).
Merci par avance pour vos réponses.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 26 mars 2014 18:04, Sylvain Henry
Bonjour,
J'avais fait un exposé pour expliquer ça entre autres, je ne sais pas si c'est tellement compréhensible sans les explications données à l'oral mais les slides sont là : http://www.sylvain-henry.info/home/data/uploads/talks/shenry-2013-02-05-haskell-intro.pdf(à partir du 20e slide).
* Contexte
Dans un langage fonctionnel, on n'utilise pas de variable globale donc pour qu'il y ait un contexte il faut le passer explicitement en entrée des fonctions et le renvoyer en sortie (en plus d'autres valeurs potentiellement) : f :: ... -> Context -> (Context, ...)
pour ne pas écrire à chaque fois les types "Context", on peut faire : type M a = Context -> (Context, a) et réécrire : f :: ... -> M a
ok, déjà ça c'est plus clair.
Il ne reste plus qu'à définir la composition pour les fonctions de ce type de sorte que le contexte soit passé de la sortie de la première fonction à l'entrée de l'autre : (>>=) :: M a -> (a -> M b) -> M b (>>=) f g = \ctx -> let (ctx2, a) = f ctx in g a ctx2
C'est là que je décroche, après un >>= on a bien une fonction qui attends un paramètre ? si oui, qui lui donne ?
Pour créer une fonction de type "M a" à partir d'un "a", on définit la fonction : return :: a -> M a return x = \ctx -> (ctx,x)
idem
* IO
Dans le cas des IO, le "contexte" est le "monde" (extérieur au programme). La fonction main a le type suivant : main :: IO () soit main :: World -> (World, () )
Donc on peut composer des fonctions qui ont des effets de bord avec (>>=) de sorte que ce soit correct d'un point de vue types sans jamais dire exactement ce que représente le type World. Les opérations s'exécutent en séquence puisque chaque opération "attend de récupérer le monde renvoyé par l'opération qui la précède" (du moins c'est l'idée). Il n'y a aucun moyen pour le programme de créer un objet de type World donc toutes les opérations s'enchainent forcément à partir de celui passé à "main".
ça ok, mais comment les mondes se "passent" entre les différents appel (bien que d'après ce que j'ai pu lire, l'IO monade est un cas particulier).
* Monades
Les monades sont juste une généralisation du mécanisme décrit ci-dessus (en gros), c'est pourquoi ils sont adaptés. La typeclass Monad implique la typeclass Applicative (l'inverse n'est pas vrai) et on aurait pu utiliser Applicative à la place de Monad dans le cas des IOs. Ça a même été suggéré sur la mailing-list haskell-cafe il n'y a pas si longtemps si je me souviens bien.
Merci, du coup les Applicative permettent aussi des effets de bords et d'enchaînements ?

Ci-dessous une intuition guidée par les types pour identifier une
différence entre les foncteurs applicatifs et les monades qui a été
mentionée auparavant par Alp.
La composition de foncteurs applicatif a pour type:
c a -> c (a -> b) -> c b
La composition de monades a pour type:
c a -> (a -> c b) -> c b
(J'ai renommé les variables f et m en c, et réordonné les paramètres pour
simplifier mon explication)
Dans les deux cas, on souhaite applique uniformément une sorte de fonction
(le second paramètre) sur une valeur en contexte (le premier paramètre).
Cependant, le type donné à la fonction lui confère un potentiel différent
dans chaque cas:
- pour Applicative, la fonction qui reçoit un a n'a pas la possibilité
d'altérer son contexte, car la fonction est dans le contexte: c (a -> b)
- pour Monad, la fonction qui reçoit un a se charge de construire le
contexte pour son résultat: a -> c b
Du coup, dans Applicative, la valeur de a peut seulement influencer la
valeur de b, mais pas la structure du contexte c.
Alors que dans Monad, la valeur de a peut influencer à la fois la valeur de
b et la structure du contexte c dans lequel b est placé.
---
Dans le cas des listes :
(<*>) :: [a -> b] -> [a] -> [b] -- j'ai remis les paramètres dans
l'ordre d'Haskell pour la suite de mon email
(>>=) :: [a] -> (a -> [b]) -> [b]
Pour (fl <*> xl), quelles que soient les valeurs dans xl, la liste
résultante aura autant d'éléments que le produit du nombre d'éléments de fl
et xl. Les fonctions contenues dans fl peuvent contrôler les valeurs dans
la liste finale, mais pas la structure de la liste finale.
Pour (xl >>= fl), les fonctions peuvent également affecter la structure
résultante.
[1,2,3] >>= (\a -> if a == 1 then [] else [a+1, a+2])
Ici, j'ai le droit de regarder la valeur, et de modifier la structure du
contexte produit (par exemple, je produis une liste vide quand la valeur
est 1, et une liste à deux éléments autrement).
---
Dans la monade IO, c'est ce qui te permet de choisir quelles actions faire
en fonction du résultat produit par les actions précédentes.
(<*>) :: IO (a -> b) -> IO a -> IO b
(>>=) :: IO a -> (a -> IO b) -> IO b
iof <*> ioa -- quelle que soit la valeur produite par ioa, on applique la
fonction dans iof
ioa >>= (\a -> ....) -- ici, dans les ... tu peux regarder a, et
effectuer différentes actions, ce qui est impossible avec Applicative
---
Corrigez-moi si je dis des bêtises... je suis malade et fatigué ! :-)
- Valentin
2014-03-26 11:41 GMT-07:00 Gautier DI FOLCO
Le 26 mars 2014 18:04, Sylvain Henry
a écrit : Bonjour,
J'avais fait un exposé pour expliquer ça entre autres, je ne sais pas si c'est tellement compréhensible sans les explications données à l'oral mais les slides sont là : http://www.sylvain-henry.info/home/data/uploads/talks/shenry-2013-02-05-haskell-intro.pdf(à partir du 20e slide).
* Contexte
Dans un langage fonctionnel, on n'utilise pas de variable globale donc pour qu'il y ait un contexte il faut le passer explicitement en entrée des fonctions et le renvoyer en sortie (en plus d'autres valeurs potentiellement) : f :: ... -> Context -> (Context, ...)
pour ne pas écrire à chaque fois les types "Context", on peut faire : type M a = Context -> (Context, a) et réécrire : f :: ... -> M a
ok, déjà ça c'est plus clair.
Il ne reste plus qu'à définir la composition pour les fonctions de ce type de sorte que le contexte soit passé de la sortie de la première fonction à l'entrée de l'autre : (>>=) :: M a -> (a -> M b) -> M b (>>=) f g = \ctx -> let (ctx2, a) = f ctx in g a ctx2
C'est là que je décroche, après un >>= on a bien une fonction qui attends un paramètre ? si oui, qui lui donne ?
Pour créer une fonction de type "M a" à partir d'un "a", on définit la fonction : return :: a -> M a return x = \ctx -> (ctx,x)
idem
* IO
Dans le cas des IO, le "contexte" est le "monde" (extérieur au programme). La fonction main a le type suivant : main :: IO () soit main :: World -> (World, () )
Donc on peut composer des fonctions qui ont des effets de bord avec (>>=) de sorte que ce soit correct d'un point de vue types sans jamais dire exactement ce que représente le type World. Les opérations s'exécutent en séquence puisque chaque opération "attend de récupérer le monde renvoyé par l'opération qui la précède" (du moins c'est l'idée). Il n'y a aucun moyen pour le programme de créer un objet de type World donc toutes les opérations s'enchainent forcément à partir de celui passé à "main".
ça ok, mais comment les mondes se "passent" entre les différents appel (bien que d'après ce que j'ai pu lire, l'IO monade est un cas particulier).
* Monades
Les monades sont juste une généralisation du mécanisme décrit ci-dessus (en gros), c'est pourquoi ils sont adaptés. La typeclass Monad implique la typeclass Applicative (l'inverse n'est pas vrai) et on aurait pu utiliser Applicative à la place de Monad dans le cas des IOs. Ça a même été suggéré sur la mailing-list haskell-cafe il n'y a pas si longtemps si je me souviens bien.
Merci, du coup les Applicative permettent aussi des effets de bords et d’enchaînements ?
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 26 mars 2014 21:19, Valentin Robert
Ci-dessous une intuition guidée par les types pour identifier une différence entre les foncteurs applicatifs et les monades qui a été mentionée auparavant par Alp.
La composition de foncteurs applicatif a pour type: c a -> c (a -> b) -> c b La composition de monades a pour type: c a -> (a -> c b) -> c b
(J'ai renommé les variables f et m en c, et réordonné les paramètres pour simplifier mon explication)
Dans les deux cas, on souhaite applique uniformément une sorte de fonction (le second paramètre) sur une valeur en contexte (le premier paramètre).
Cependant, le type donné à la fonction lui confère un potentiel différent dans chaque cas: - pour Applicative, la fonction qui reçoit un a n'a pas la possibilité d'altérer son contexte, car la fonction est dans le contexte: c (a -> b) - pour Monad, la fonction qui reçoit un a se charge de construire le contexte pour son résultat: a -> c b
Du coup, dans Applicative, la valeur de a peut seulement influencer la valeur de b, mais pas la structure du contexte c. Alors que dans Monad, la valeur de a peut influencer à la fois la valeur de b et la structure du contexte c dans lequel b est placé.
---
Dans le cas des listes :
(<*>) :: [a -> b] -> [a] -> [b] -- j'ai remis les paramètres dans l'ordre d'Haskell pour la suite de mon email (>>=) :: [a] -> (a -> [b]) -> [b]
Pour (fl <*> xl), quelles que soient les valeurs dans xl, la liste résultante aura autant d'éléments que le produit du nombre d'éléments de fl et xl. Les fonctions contenues dans fl peuvent contrôler les valeurs dans la liste finale, mais pas la structure de la liste finale.
Pour (xl >>= fl), les fonctions peuvent également affecter la structure résultante.
[1,2,3] >>= (\a -> if a == 1 then [] else [a+1, a+2])
Ici, j'ai le droit de regarder la valeur, et de modifier la structure du contexte produit (par exemple, je produis une liste vide quand la valeur est 1, et une liste à deux éléments autrement).
---
Dans la monade IO, c'est ce qui te permet de choisir quelles actions faire en fonction du résultat produit par les actions précédentes.
(<*>) :: IO (a -> b) -> IO a -> IO b (>>=) :: IO a -> (a -> IO b) -> IO b
iof <*> ioa -- quelle que soit la valeur produite par ioa, on applique la fonction dans iof ioa >>= (\a -> ....) -- ici, dans les ... tu peux regarder a, et effectuer différentes actions, ce qui est impossible avec Applicative
---
Corrigez-moi si je dis des bêtises... je suis malade et fatigué ! :-)
- Valentin
Ça me paraît plus clair, pour être sur : * une Applicative me permet de réunir inconditionnellement deux expression au sein d'un même contexte (une sorte de write only) * une Monade me permet de lire la valeur d'un contexte et dans recréer un à ma guise, comme je le souhaite (une sorte de read, drop, create) Merci.

Non je ne dirais pas ça.
L'idée générale est "comment peut-on appliquer fonctions en tenant compte
d'un certain contexte ?".
Une solution (fournie par Functor) est d'appliquer une fonction pure de
tout contexte (a -> b) directement sur un contexte en entrée (c a) :
c a -> (a -> b) -> c b
Dans ce cas, la structure du contexte de sortie est uniquement dépendante
de la structure du contexte de l'entrée, jamais de la fonction.
Une solution (fournie par Applicative) est d'appliquer une fonction pure
placée dans un contexte (c (a -> b)) sur l'entrée :
c a -> c (a -> b) -> c b
Dans ce cas, la structure du contexte de sortie est déterminée par le
contexte de l'entrée et le contexte de la fonction, mais toujours pas par
la fonction.
Une solution (fournie par Monad) est d'appliquer une fonciton (a -> c b)
qui produit, pour la valeur placée dans le contexte de l'entrée (le a), une
autre valeur dans un autre contexte (le (c b)) :
c a -> (a -> c b) -> c b
Dans ce cas, la structure du contexte de sortie peut être influencé par la
fonction, qui peut dépendre de la valeur dans le contexte d'entrée.
Je n'utiliserai pas le vocabulaire read/write/drop/create car je ne pense
pas que ça aidera à éclaircir les idées. Tout comme je n'utiliserai pas
d'analogie, car elles marchent souvent bien pour un exemple et très mal
pour un autre. Parler de contexte est déjà à la limite du douteux pour
certaines monades...
Si cela n'est pas très clair, ne te tracasse pas trop, et essaie de lire ou
écrire quelques programmes applicatifs et monadiques pour certaines
monades, puis de lire ou écrire quelques programmes pour des monades
génériques, et tu finiras par discerner les motifs sous-jacents.
- Valentin
2014-03-26 15:12 GMT-07:00 Gautier DI FOLCO
Le 26 mars 2014 21:19, Valentin Robert
a écrit : Ci-dessous une intuition guidée par les types pour identifier une
différence entre les foncteurs applicatifs et les monades qui a été mentionée auparavant par Alp.
La composition de foncteurs applicatif a pour type: c a -> c (a -> b) -> c b La composition de monades a pour type: c a -> (a -> c b) -> c b
(J'ai renommé les variables f et m en c, et réordonné les paramètres pour simplifier mon explication)
Dans les deux cas, on souhaite applique uniformément une sorte de fonction (le second paramètre) sur une valeur en contexte (le premier paramètre).
Cependant, le type donné à la fonction lui confère un potentiel différent dans chaque cas: - pour Applicative, la fonction qui reçoit un a n'a pas la possibilité d'altérer son contexte, car la fonction est dans le contexte: c (a -> b) - pour Monad, la fonction qui reçoit un a se charge de construire le contexte pour son résultat: a -> c b
Du coup, dans Applicative, la valeur de a peut seulement influencer la valeur de b, mais pas la structure du contexte c. Alors que dans Monad, la valeur de a peut influencer à la fois la valeur de b et la structure du contexte c dans lequel b est placé.
---
Dans le cas des listes :
(<*>) :: [a -> b] -> [a] -> [b] -- j'ai remis les paramètres dans l'ordre d'Haskell pour la suite de mon email (>>=) :: [a] -> (a -> [b]) -> [b]
Pour (fl <*> xl), quelles que soient les valeurs dans xl, la liste résultante aura autant d'éléments que le produit du nombre d'éléments de fl et xl. Les fonctions contenues dans fl peuvent contrôler les valeurs dans la liste finale, mais pas la structure de la liste finale.
Pour (xl >>= fl), les fonctions peuvent également affecter la structure résultante.
[1,2,3] >>= (\a -> if a == 1 then [] else [a+1, a+2])
Ici, j'ai le droit de regarder la valeur, et de modifier la structure du contexte produit (par exemple, je produis une liste vide quand la valeur est 1, et une liste à deux éléments autrement).
---
Dans la monade IO, c'est ce qui te permet de choisir quelles actions faire en fonction du résultat produit par les actions précédentes.
(<*>) :: IO (a -> b) -> IO a -> IO b (>>=) :: IO a -> (a -> IO b) -> IO b
iof <*> ioa -- quelle que soit la valeur produite par ioa, on applique la fonction dans iof ioa >>= (\a -> ....) -- ici, dans les ... tu peux regarder a, et effectuer différentes actions, ce qui est impossible avec Applicative
---
Corrigez-moi si je dis des bêtises... je suis malade et fatigué ! :-)
- Valentin
Ça me paraît plus clair, pour être sur : * une Applicative me permet de réunir inconditionnellement deux expression au sein d'un même contexte (une sorte de write only) * une Monade me permet de lire la valeur d'un contexte et dans recréer un à ma guise, comme je le souhaite (une sorte de read, drop, create)
Merci.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 27 mars 2014 01:37, Valentin Robert
Non je ne dirais pas ça.
L'idée générale est "comment peut-on appliquer fonctions en tenant compte d'un certain contexte ?".
Une solution (fournie par Functor) est d'appliquer une fonction pure de tout contexte (a -> b) directement sur un contexte en entrée (c a) : c a -> (a -> b) -> c b Dans ce cas, la structure du contexte de sortie est uniquement dépendante de la structure du contexte de l'entrée, jamais de la fonction.
Une solution (fournie par Applicative) est d'appliquer une fonction pure placée dans un contexte (c (a -> b)) sur l'entrée : c a -> c (a -> b) -> c b Dans ce cas, la structure du contexte de sortie est déterminée par le contexte de l'entrée et le contexte de la fonction, mais toujours pas par la fonction.
Une solution (fournie par Monad) est d'appliquer une fonciton (a -> c b) qui produit, pour la valeur placée dans le contexte de l'entrée (le a), une autre valeur dans un autre contexte (le (c b)) : c a -> (a -> c b) -> c b Dans ce cas, la structure du contexte de sortie peut être influencé par la fonction, qui peut dépendre de la valeur dans le contexte d'entrée.
Je n'utiliserai pas le vocabulaire read/write/drop/create car je ne pense pas que ça aidera à éclaircir les idées. Tout comme je n'utiliserai pas d'analogie, car elles marchent souvent bien pour un exemple et très mal pour un autre. Parler de contexte est déjà à la limite du douteux pour certaines monades...
Si cela n'est pas très clair, ne te tracasse pas trop, et essaie de lire ou écrire quelques programmes applicatifs et monadiques pour certaines monades, puis de lire ou écrire quelques programmes pour des monades génériques, et tu finiras par discerner les motifs sous-jacents.
- Valentin
C'est un peu plus claire, merci.

Hi,
Sorry for writing in english, I am not from France.
Take a look here, please:
http://www.haskell.org/haskellwiki/Modular_Monadic_Compilers_for_Programming...
The source at #3) on the above page is a code generator written using the State monad.
The state monad is necessary because here it provides a sort of context, like a set of the global variables
in imperative programming.
And that is why we are using The State Monad, to simulate the
use of a set of global (or local) variables, used to store values.
Especially in this example, the lenghts of the pieces of generated code are stored in order to compute the length of the biggers codes, when they are catenated, (glued together).
Other monads have specific use. Parser monad is used for glueing parsers, the list monad can simulate paralel computations and even backtracking (there is a backtracking monad too), the Maybe monad is used to make computations including Nothing in the set of values, etc.
Basically monadic capsules can be seen like Christmas Giftts containing compuations. And monadic operators have two kind of use:
- return is a package maker, it creates a capsule, a package, like wrapping a gift
- bind is something like: how to combine a function with a package. Open the package, compose functions, take care to produce an other package (or capsule).
Hoping it helps,
Sincerely yours,
Dan Popa
----- Forwarded Message -----
From: Gautier DI FOLCO

2014-03-27 6:07 GMT+01:00 Dan Popa
Hi,
Sorry for writing in english, I am not from France. Take a look here, please:
http://www.haskell.org/haskellwiki/Modular_Monadic_Compilers_for_Programming...
The source at #3) on the above page is a code generator written using the State monad. The state monad is necessary because here it provides a sort of context, like a set of the global variables in imperative programming. And that is why we are using The State Monad, to simulate the use of a set of global (or local) variables, used to store values.
Especially in this example, the lenghts of the pieces of generated code are stored in order to compute the length of the biggers codes, when they are catenated, (glued together).
Other monads have specific use. Parser monad is used for glueing parsers, the list monad can simulate paralel computations and even backtracking (there is a backtracking monad too), the Maybe monad is used to make computations including Nothing in the set of values, etc.
Basically monadic capsules can be seen like Christmas Giftts containing compuations. And monadic operators have two kind of use: - return is a package maker, it creates a capsule, a package, like wrapping a gift - bind is something like: how to combine a function with a package. Open the package, compose functions, take care to produce an other package (or capsule).
Hoping it helps, Sincerely yours, Dan Popa
----- Forwarded Message ----- *From:* Gautier DI FOLCO
*To:* La liste Haskell Francophone *Sent:* Wednesday, March 26, 2014 12:10 PM *Subject:* [Haskell-fr] Éclaircissements sur les monades Bonjour,
Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative).
Merci par avance pour vos réponses.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
State Monads are clearer to me, thanks.

2014-03-27 10:11 GMT+01:00 Gautier DI FOLCO
2014-03-27 6:07 GMT+01:00 Dan Popa
: Hi,
Sorry for writing in english, I am not from France. Take a look here, please:
http://www.haskell.org/haskellwiki/Modular_Monadic_Compilers_for_Programming...
The source at #3) on the above page is a code generator written using the State monad. The state monad is necessary because here it provides a sort of context, like a set of the global variables in imperative programming. And that is why we are using The State Monad, to simulate the use of a set of global (or local) variables, used to store values.
Especially in this example, the lenghts of the pieces of generated code are stored in order to compute the length of the biggers codes, when they are catenated, (glued together).
Other monads have specific use. Parser monad is used for glueing parsers, the list monad can simulate paralel computations and even backtracking (there is a backtracking monad too), the Maybe monad is used to make computations including Nothing in the set of values, etc.
Basically monadic capsules can be seen like Christmas Giftts containing compuations. And monadic operators have two kind of use: - return is a package maker, it creates a capsule, a package, like wrapping a gift - bind is something like: how to combine a function with a package. Open the package, compose functions, take care to produce an other package (or capsule).
Hoping it helps, Sincerely yours, Dan Popa
----- Forwarded Message ----- *From:* Gautier DI FOLCO
*To:* La liste Haskell Francophone *Sent:* Wednesday, March 26, 2014 12:10 PM *Subject:* [Haskell-fr] Éclaircissements sur les monades Bonjour,
Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative).
Merci par avance pour vos réponses.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
State Monads are clearer to me, thanks.
Bonjour à tous, J'ai fais pas mal de progrès ces derniers temps (de manière globale) du coup il y a encore une zone d'ombre que j'aimerais éclaircir : comment les valeurs sont passées à une fonction ? La question est vague, un exemple est de rigueur, j'ai ce code que je pense assez classique : -- Generic newtype Reader r a = Reader { runReader :: r -> a } instance Monad (Reader r) where return a = Reader $ \_ -> a m >>= k = Reader $ \r -> runReader (k (runReader m r)) r ask :: Reader a a ask = Reader id asks :: (r -> a) -> Reader r a asks f = Reader f local :: (r -> b) -> Reader b a -> Reader r a local f m = Reader $ runReader m . f -- Specific data MyState = MyState { foo :: String , bar :: Int } deriving (Show) computation :: Reader MyState (Maybe String) computation = do n <- asks bar x <- asks foo if n > 0 then return (Just x) else return Nothing example1 :: Maybe String example1 = runReader computation $ MyState "hello!" 1 example2 :: Maybe String example2 = runReader computation $ MyState "example!" 0 En gros j'ai deux soucis : comment runReader et asks fonctionnent ? Voilà mon raisonnement : Ça c'est notre but : runReader (asks bar) (MyState "example!" 0) :: Int runReader (asks bar) :: MyState -> Int => ok, on attends un MyState pour évaluer le tout runReader :: Reader r a -> r -> a => pas ok : avec un Reader (qui contient une fonction r -> a) on sort cette fonction et on l'applique Question 1 : comment on applique cette fonction encapsulée ? (asks bar) :: Reader MyState Int => pas ok : on attends un MyState pour sortir un Int Question 2 : comment ce MyState est "passé" et à quoi pour que ça sorte un Int ? Jamais deux sans trois (même si je pense que répondre à la première question répondra mécaniquement aux deux autres). Question 3 : Pour computation, comment le Mystate est "passé" aux différents asks ? ou à l'envers, comment les asks savent où et quoi chercher ? (et comment l'appliquer) J'ai tenté le code suivant, sans succès : computation :: Reader MyState (Maybe String) computation m = do n <- asks bar m x <- asks foo m if n > 0 then return (Just x) else return Nothing Merci par avance pour vos réponses et votre patience.

computation m = …
ne peut pas fonctionner puisque computation à la type Reader … donc ne prend pas d’argument: tout est encapsulé dans la fonction runReader.
La magie provient de >>= qui, d’après ta définition, construit un nouveau reader en chaînant et décapsulant la première monade et la continuation (k): on applique la fonction contenue dans Reader avec (runReader m r), on produit une nouvelle monade en applicant k, on décapsule cette deuxième monade avec le descend appel à runReader. Tout ça est une fonction qui est encapsulée dans un Reader, donc qui devient le champ runReader de Reader.
Donc pour répondre à tes questions:
Q1: la fonction est un champ de Reader accessible avec runReader :: r -> a, donc tu extraies cette fonction du reader, et ensuite tu peux lui passer un argument.
Q2: cf. le chaînage ci-dessus. Le MyState est passé lors de la première invocation à runReader qui va dérouler la “computation”
Q3: pareil.
Le “principe”de reader et de beaucoup de code monadique est de construire des expressions, qui ne sont évaluées qu’au toplevel, en passant un argument à runXXX pour “démarrer” la chaîne de calcul. IO est un cas particulier qui démarre par l’appel à main. Tu ne peux pas (sauf magie) extraire une valeur de IO, alors que tu peux extraire une valeur (une fonction) depuis un Reader.
HTH
Arnaud
On 18 May 2014, at 12:04, Gautier DI FOLCO
2014-03-27 10:11 GMT+01:00 Gautier DI FOLCO
: 2014-03-27 6:07 GMT+01:00 Dan Popa : Hi,
Sorry for writing in english, I am not from France. Take a look here, please: http://www.haskell.org/haskellwiki/Modular_Monadic_Compilers_for_Programming...
The source at #3) on the above page is a code generator written using the State monad. The state monad is necessary because here it provides a sort of context, like a set of the global variables in imperative programming. And that is why we are using The State Monad, to simulate the use of a set of global (or local) variables, used to store values.
Especially in this example, the lenghts of the pieces of generated code are stored in order to compute the length of the biggers codes, when they are catenated, (glued together).
Other monads have specific use. Parser monad is used for glueing parsers, the list monad can simulate paralel computations and even backtracking (there is a backtracking monad too), the Maybe monad is used to make computations including Nothing in the set of values, etc.
Basically monadic capsules can be seen like Christmas Giftts containing compuations. And monadic operators have two kind of use: - return is a package maker, it creates a capsule, a package, like wrapping a gift - bind is something like: how to combine a function with a package. Open the package, compose functions, take care to produce an other package (or capsule).
Hoping it helps, Sincerely yours, Dan Popa
----- Forwarded Message ----- From: Gautier DI FOLCO
To: La liste Haskell Francophone Sent: Wednesday, March 26, 2014 12:10 PM Subject: [Haskell-fr] Éclaircissements sur les monades Bonjour,
Vaste sujet en perspective : les monades. Je pense avoir compris le principe des monades (être en mesure d'effectuer des actions sur une valeur au sein d'un contexte), en revanche il y a deux choses que je ne comprends pas : * La notion de contexte (ou de cadre de calcul) est un peu floue * Pourquoi est-ce que les monades sont aptes à contenir les effets de bords (j'ai lu effectful computations) et pas d'autres typeclass (comme Applicative).
Merci par avance pour vos réponses.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
State Monads are clearer to me, thanks.
Bonjour à tous,
J'ai fais pas mal de progrès ces derniers temps (de manière globale) du coup il y a encore une zone d'ombre que j'aimerais éclaircir : comment les valeurs sont passées à une fonction ?
La question est vague, un exemple est de rigueur, j'ai ce code que je pense assez classique :
-- Generic
newtype Reader r a = Reader { runReader :: r -> a }
instance Monad (Reader r) where return a = Reader $ \_ -> a m >>= k = Reader $ \r -> runReader (k (runReader m r)) r
ask :: Reader a a ask = Reader id
asks :: (r -> a) -> Reader r a asks f = Reader f
local :: (r -> b) -> Reader b a -> Reader r a local f m = Reader $ runReader m . f
-- Specific
data MyState = MyState { foo :: String , bar :: Int } deriving (Show)
computation :: Reader MyState (Maybe String) computation = do n <- asks bar x <- asks foo if n > 0 then return (Just x) else return Nothing
example1 :: Maybe String example1 = runReader computation $ MyState "hello!" 1
example2 :: Maybe String example2 = runReader computation $ MyState "example!" 0
En gros j'ai deux soucis : comment runReader et asks fonctionnent ?
Voilà mon raisonnement :
Ça c'est notre but : runReader (asks bar) (MyState "example!" 0) :: Int runReader (asks bar) :: MyState -> Int => ok, on attends un MyState pour évaluer le tout
runReader :: Reader r a -> r -> a => pas ok : avec un Reader (qui contient une fonction r -> a) on sort cette fonction et on l'applique Question 1 : comment on applique cette fonction encapsulée ?
(asks bar) :: Reader MyState Int => pas ok : on attends un MyState pour sortir un Int Question 2 : comment ce MyState est "passé" et à quoi pour que ça sorte un Int ?
Jamais deux sans trois (même si je pense que répondre à la première question répondra mécaniquement aux deux autres). Question 3 : Pour computation, comment le Mystate est "passé" aux différents asks ? ou à l'envers, comment les asks savent où et quoi chercher ? (et comment l'appliquer) J'ai tenté le code suivant, sans succès : computation :: Reader MyState (Maybe String) computation m = do n <- asks bar m x <- asks foo m if n > 0 then return (Just x) else return Nothing
Merci par avance pour vos réponses et votre patience. _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 18 mai 2014 13:49, Arnaud Bailly
computation m = …
ne peut pas fonctionner puisque computation à la type Reader … donc ne prend pas d’argument: tout est encapsulé dans la fonction runReader.
La magie provient de >>= qui, d’après ta définition, construit un nouveau reader en chaînant et décapsulant la première monade et la continuation (k): on applique la fonction contenue dans Reader avec (runReader m r), on produit une nouvelle monade en applicant k, on décapsule cette deuxième monade avec le descend appel à runReader. Tout ça est une fonction qui est encapsulée dans un Reader, donc qui devient le champ runReader de Reader.
Raaah, je l'avais oublié (le >>=), effectivement, tout s'éclaire !
Donc pour répondre à tes questions:
Q1: la fonction est un champ de Reader accessible avec runReader :: r -> a, donc tu extraies cette fonction du reader, et ensuite tu peux lui passer un argument. Q2: cf. le chaînage ci-dessus. Le MyState est passé lors de la première invocation à runReader qui va dérouler la “computation” Q3: pareil.
Le “principe”de reader et de beaucoup de code monadique est de construire des expressions, qui ne sont évaluées qu’au toplevel, en passant un argument à runXXX pour “démarrer” la chaîne de calcul. IO est un cas particulier qui démarre par l’appel à main. Tu ne peux pas (sauf magie) extraire une valeur de IO, alors que tu peux extraire une valeur (une fonction) depuis un Reader.
ok, compris, merci.

On 05/18/2014 12:04 PM, Gautier DI FOLCO wrote:
Merci par avance pour vos réponses et votre patience.
A mon avis, le plus efficace pour vraiment comprendre ce qu'il se passe, c'est de désucrer le code à la main. Pour moi le truc qui m'a vraiment permis d'avancer ça a été d'écrire "State" et "Maybe" moi même, juste en suivant les types, puis de faire "StateT" et "MaybeT". Il y a ce "cours", qui est en fait une succession d'exercices, qui je pense est excellent : https://github.com/NICTA/course

Le 18 mai 2014 14:36, Simon Marechal
On 05/18/2014 12:04 PM, Gautier DI FOLCO wrote:
Merci par avance pour vos réponses et votre patience.
A mon avis, le plus efficace pour vraiment comprendre ce qu'il se passe, c'est de désucrer le code à la main. Pour moi le truc qui m'a vraiment permis d'avancer ça a été d'écrire "State" et "Maybe" moi même, juste en suivant les types, puis de faire "StateT" et "MaybeT".
Il y a ce "cours", qui est en fait une succession d'exercices, qui je pense est excellent :
ça a l'air génial ce dépôt, merci.

Je plussoie sur le cours NICTA que j’ai découvert récemment.
Tony Morris est un génie, dommage que ce soit aussi un connard.
On 18 May 2014, at 15:20, Gautier DI FOLCO
Le 18 mai 2014 14:36, Simon Marechal
a écrit : On 05/18/2014 12:04 PM, Gautier DI FOLCO wrote: Merci par avance pour vos réponses et votre patience.
A mon avis, le plus efficace pour vraiment comprendre ce qu'il se passe, c'est de désucrer le code à la main. Pour moi le truc qui m'a vraiment permis d'avancer ça a été d'écrire "State" et "Maybe" moi même, juste en suivant les types, puis de faire "StateT" et "MaybeT".
Il y a ce "cours", qui est en fait une succession d'exercices, qui je pense est excellent :
https://github.com/NICTA/course
ça a l'air génial ce dépôt, merci. _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

Le 18 mai 2014 15:27, Arnaud Bailly
Je plussoie sur le cours NICTA que j’ai découvert récemment.
Tony Morris est un génie, dommage que ce soit aussi un connard.
On 18 May 2014, at 15:20, Gautier DI FOLCO
wrote: Le 18 mai 2014 14:36, Simon Marechal
a écrit : On 05/18/2014 12:04 PM, Gautier DI FOLCO wrote:
Merci par avance pour vos réponses et votre patience.
A mon avis, le plus efficace pour vraiment comprendre ce qu'il se passe, c'est de désucrer le code à la main. Pour moi le truc qui m'a vraiment permis d'avancer ça a été d'écrire "State" et "Maybe" moi même, juste en suivant les types, puis de faire "StateT" et "MaybeT".
Il y a ce "cours", qui est en fait une succession d'exercices, qui je pense est excellent :
ça a l'air génial ce dépôt, merci. _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
Je suis de retour... Je suis sur le cours de NICTA et je suis sur la définition de l'instance Apply de State. Voilà à quoi j'arrive : State f <*> State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d) Ce qui n'est pas très éloigné de la solution ( https://github.com/tonymorris/course/blob/master/src/Course/State.hs#L45) : instance Apply (State s) where State f <*> State a = State (\s -> let (g, t) = f s (z, u) = a t in (g z, u)) Si je les met côte à côte : State f <*> State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d) State f <*> State g = State $ \x -> let (c, d) = f x in let (a, b) = g d in (c a, b) Et là, je suis troublé... J'inverse l'évaluation de f et de g et ça continue de fonctionner (selon les tests et 2-3 cas fait à la main). J'ai trois questions : 1. Est-ce que j'ai bon ? 2. Si oui, pourquoi en général on fait plus comme la solution ? 3. Si oui (1), pourquoi est-ce que les deux sont équivalents ? Merci par avance.

As-tu testé avec des fonctions f et g qui ne commutent pas ?
- Valentin
2014-05-24 11:03 GMT-07:00 Gautier DI FOLCO
Le 18 mai 2014 15:27, Arnaud Bailly
a écrit : Je plussoie sur le cours NICTA que j’ai découvert récemment.
Tony Morris est un génie, dommage que ce soit aussi un connard.
On 18 May 2014, at 15:20, Gautier DI FOLCO
wrote: Le 18 mai 2014 14:36, Simon Marechal
a écrit : On 05/18/2014 12:04 PM, Gautier DI FOLCO wrote:
Merci par avance pour vos réponses et votre patience.
A mon avis, le plus efficace pour vraiment comprendre ce qu'il se passe, c'est de désucrer le code à la main. Pour moi le truc qui m'a vraiment permis d'avancer ça a été d'écrire "State" et "Maybe" moi même, juste en suivant les types, puis de faire "StateT" et "MaybeT".
Il y a ce "cours", qui est en fait une succession d'exercices, qui je pense est excellent :
ça a l'air génial ce dépôt, merci. _______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
Je suis de retour...
Je suis sur le cours de NICTA et je suis sur la définition de l'instance Apply de State. Voilà à quoi j'arrive : State f <*> State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d)
Ce qui n'est pas très éloigné de la solution ( https://github.com/tonymorris/course/blob/master/src/Course/State.hs#L45) :
instance Apply (State s) where State f <*> State a = State (\s -> let (g, t) = f s (z, u) = a t in (g z, u))
Si je les met côte à côte : State f <*> State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d) State f <*> State g = State $ \x -> let (c, d) = f x in let (a, b) = g d in (c a, b)
Et là, je suis troublé... J'inverse l'évaluation de f et de g et ça continue de fonctionner (selon les tests et 2-3 cas fait à la main).
J'ai trois questions : 1. Est-ce que j'ai bon ? 2. Si oui, pourquoi en général on fait plus comme la solution ? 3. Si oui (1), pourquoi est-ce que les deux sont équivalents ?
Merci par avance.
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr

On 05/24/2014 08:49 PM, Gautier DI FOLCO wrote:
Le 24 mai 2014 20:22, Valentin Robert
mailto:valentin.robert.42@gmail.com> a écrit : As-tu testé avec des fonctions f et g qui ne commutent pas ?
J'ai tenté ça : runState (pure (-) <*> pure 4 <*> pure 2) 7
Essaie plutôt avec quelque chose qui manipule l'état !

On 05/24/2014 08:49 PM, Gautier DI FOLCO wrote:
J'ai tenté ça : runState (pure (-) <*> pure 4 <*> pure 2) 7
State f `ap1` State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d) State f `ap2` State g = State $ \x -> let (c, d) = f x in let (a, b) = g d in (c a, b) *Xxxx> runState (pure (,) `ap2` put 4 `ap2` put 7) 0 (((),()),7) *Xxxx> runState (pure (,) `ap1` put 4 `ap1` put 7) 0 (((),()),4)

2014-05-24 21:01 GMT+02:00 Simon Marechal
On 05/24/2014 08:49 PM, Gautier DI FOLCO wrote:
J'ai tenté ça : runState (pure (-) <*> pure 4 <*> pure 2) 7
State f `ap1` State g = State $ \x -> let (a, b) = g x in let (c, d) = f b in (c a, d) State f `ap2` State g = State $ \x -> let (c, d) = f x in let (a, b) = g d in (c a, b)
*Xxxx> runState (pure (,) `ap2` put 4 `ap2` put 7) 0 (((),()),7) *Xxxx> runState (pure (,) `ap1` put 4 `ap1` put 7) 0 (((),()),4)
_______________________________________________ Haskell-fr mailing list Haskell-fr@haskell.org http://www.haskell.org/mailman/listinfo/haskell-fr
ok, je suis suis vautré, j'aime mieux ça ^^ Merci.
participants (7)
-
Alp Mestanogullari
-
Arnaud Bailly
-
Dan Popa
-
Gautier DI FOLCO
-
Simon Marechal
-
Sylvain Henry
-
Valentin Robert