Réponse dans le texte :


2013/12/20 Gautier DI FOLCO <gautier.difolco@gmail.com>
Bonjour,

Cela fait maintenant quelques mois que je côtoie Haskell, j'arrive presque toujours à mes fins, mais cela ne me convient pas, je tente donc de prendre le problème à sa racine : la compréhension, ou plutôt l'assimilation des concepts. Je pense que j'ai un soucis à ce niveau car je pense plutôt bien voir comment tout s'emboîte, mais face au code, je suis incapable de l'appliquer ! Je vous laisse juger par vous-même.
Je vais tenter d'expliquer ma manière de voir les choses de manière aussi détaillée que possible via un ensemble d'assertions (notées AN où N est le numéro de l'assertion).
Le typeclass Applicative à la tête suivante :
class Functor f => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
  (*>) :: f a -> f b -> f b
  (<*) :: f a -> f b -> f a

A1 : Ce qui signifie que tout type implémentant ces fonctions doivent également implémenter les fonctions de Functor.
Q1 : quelle est l'intérêt ?

Ce n'est pas vraiment par intérêt plutôt qu'une conséquence du fait qu'un foncteur applicatif a un foncteur sous-jacent. En particulier, le foncteur est utilisé dans l'implémentation par défaut de (*>) et (<*), et une instance correcte doit satisfaire la loi :

f <$> x = pure f <*> x

On pourrait s'en passer, mais c'est probablement pratique.
 
Nous avons pure :: Applicative f => a -> f a
A2 : 'f a' signifie de type a implémentant le typeclass f (Applicative dans ce cas précis

Hmm pas vraiment. `f a` signifie de type `f a`, où `f` est un foncteur applicatif.
 
A3 : Il s'agit "simplement" d'un lifting, d'une encapsulation, grosso-modo de donner un paramètre à un constructeur.

Ensuite (<*>) :: Applicative f => f (a -> b) -> f a -> f b
A4 : prend une fonction et un paramètre dans un "emballage" implémentant Applicative et applique cette valeur à cette fonction en retournant le type dans un autre "emballage".
A5 : cette fonction sert dans deux cas : à fournir un moyen de manipuler le contenu de "boîtes", "décurryfier" dans le sens passer des arguments au fur et à mesure à une fonction.
Q2 : Y a-t-il d'autres cas d'application.

Pas vraiment, c'est juste un "lift" de l'application d'une fonction. Donc à utiliser quand tu as une fonction à appliquer...

A6 : <* et *> ne servent qu'à appliquer un des arguments.
Q3 : quels sont les cas d'applications typiques ? j'ai du mal à voir

Je ne fais pas assez de Haskell pour savoir où c'est utilisé, mais c'est probablement passé comme argument à une fonction d'ordre supérieur dans des cas spéciaux où on n'a pas trop de choix sur le format des données en entrée et on veut ignorer la moitié...

Ensuite il y a les fonctions de lifting :

liftA :: Applicative f => (a -> b) -> f a -> f b
A7 : prend une fonction "normale" à un argument et en fait une fonction maniant une structure encapsulée.
Q4 : quel est l'intérêt ? liftA (+1) $ Just 1 n'est pas égale à pure (+1) <*> Just 1 ?

Si, mais parfois tu as besoin de `pure` et parfois de `liftA`. Encore une fois, ça dépend de la façon dont c'est utilisé. Évidemment, si tu appliques tout complètement ça semble redondant, mais quand tu appliques partiellement, `liftA (+1)` et `pure (+1)` ont un type différent, tous deux utiles.
 
Q5 : ou à la version Functor : (+1) <$> Just 1 ?
Q6 : dans le cas de version Functor, quel est l'intérêt de la passer Applicative ?

Pas d'intérêt : si tout ce dont tu as besoin c'est `fmap`, utilise `fmap`.
 
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
A8 : idem mais avec deux arguments
A9 : liftA2 (+) (Just 2) (Just 1) est équivalent à liftA (+) (Just 2) <*> Just 1
Q7 : Quel est l'intérêt du coup ?

Même réponse. Tu sembles chercher à éviter toute redondance. Pourquoi ne pas écrire tout ton code avec juste des lambdas et des applications ? :-)
 
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
A10 : idem mais avec trois arguments

Q8 : Quel est l'intérêt de Control.Applicative.Lift ?
Il est écrit : "Adding a new kind of pure computation to an applicative functor"
Q9 : J'ai du mal à saisir cette notion de calcul, est-ce qu'il est possible de me  l'expliquer en deux mots ?

Je ne saurai expliquer ça clairement, mais tu peux jeter un coup d'oeil ici :

http://hackage.haskell.org/package/transformers-0.3.0.0/docs/src/Control-Applicative-Lift.html#unLift

En particulier, l'implémentation de `fmap` et le commentaire du type Errors peuvent te donner une idée de ce qu'il se passe...

Je me rend compte que mon mail est très long, ça fait plus de deux mois que je me casse les dents dessus, j'ai écumé plusieurs articles/livres, pas moyen de me faire une idée claire de tout ça, ou de voir les applications pratiques.
Je vous serait très reconnaissant de prendre le temps de me répondre et/ou de valider/invalider mes assertions,
Merci par avance.

PS : Par la suite j'aurais des question sur Alternative et les Monades
PPS : la longueur de la rédaction m'a donné une idée pour Q7 :
A11 : liftA2 et liftA3 servent à obtenir des fonctions "passables" à des fonctions qui le demande, ex :
myF :: (Maybe a -> Maybe b -> Maybe c) -> a -> b -> Maybe c
myF f a b = f (Just a) (Just  b)
la fonction serait appelable via myF (liftA2 (+)) 1 2

Exactement !

_______________________________________________
Haskell-fr mailing list
Haskell-fr@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-fr