[Haskell-fr] Éclaircissement sur les Applicatives

Alp Mestanogullari alpmestan at gmail.com
Sat Dec 21 01:57:57 UTC 2013


Salut,

Réponse dans le texte aussi. Je réponds à ton dernier mail et après je
relirai le premier pour voir si je peux apporter un autre angle que
Valentin, etc.

2013/12/20 Gautier DI FOLCO <gautier.difolco at gmail.com>
>
>  2013/12/20 Gautier DI FOLCO <gautier.difolco at gmail.com>
>>
>>> 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.
>>
>
> Donc pour 'f a', 'f' est un type paramétrique qui implémente le typeclass
> Applicative et qui a un paramètre 'a' ?
>

Oui tout à fait. 'f' peut être IO, [ ], Maybe, Either String (sans le 2è
type en paramètre - oui c'est un début de réponse à ta seconde question)
...

Ça signifie donc que les types n'ayant pas ou plusieurs paramètre de type
> ne peuvent pas être utilisé dans cette fonction ? Comme Either ?
>

Bah comme je le dis justement entre parenthèses, pour Either, il y a un
problème au niveau des "kinds" (grosso modo les type des types). Au vu de
la gueule d'Applicative, 'f' ne doit prendre qu'un type en paramètre. Mais
'f' peut être un type paramétré à 45 paramètres, les 44 premiers étant
fixés à des types précis ou pas forcément:

data Blah a b c d = Blah

instance Applicative (Blah a b c) where
   ...

data Foo a b c = Foo

instance Applicative (Foo Int String) where -- ça nécessite {-# LANGUAGE
FlexibleInstances #-}, je peux t'expliquer pourquoi si ça t'intéresse.
  ...

Et donc, si tu donnes un premier paramètre à Either et que tu laisses
varier le second, tu obtiens un Applicative :-)

C'est plus clair ?

 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 ? :-)
>>
>
> En fait je cherche à savoir pourquoi il y a plusieurs formes, mais
> visiblement je me pose trop de questions :/
>

C'est pas commun de pouvoir écrire quelque chose de 15 manières différentes
dès qu'on a un peu de "structure" (savoir que tel type implémente telle
typeclass, tout ça). Desfois c'est du simple "sucre syntaxique", desfois
c'est deux concepts qui coincident, desfois autre chose. Te prends pas trop
la tête avec ça à mon avis.


> Même une piste m'aiderais :/
> J'ai l'impression qu'il s'agit de gérer soit une valeur soit une valeur et
> une fonction à lui appliquer, mais en quoi la valeur seule serait Pure ?
> Qu'est-ce que pure dans ce contexte ?
>

En gros, il s'agit de décorer des valeurs quelconques avec d'autres valeurs
*qui sont dans un applicative". Ou vu dans l'autre sens, il s'agit de
décorer un applicative avec des valeurs pures. Par "décorer" j'entends
"mettre ensemble avec".

L'exemple des erreurs dont parle Valentin est en fait assez simple si on
arrive à se focaliser sur chaque truc un à un.

Déjà, Lift:

> data Lift f a = Pure a | Other (f a)

On va considérer Lift appliqué un 'f' donné, par exemple Maybe.

> data LiftMaybe a = Pure a | Other (Maybe a)

Donc c'est comme si on avait collé tous les 'Maybe a' avec les 'a' tout
court, en disant quand même qu'on se rappelle d'où chacun vient grâce aux
constructeurs. Tu noteras que le constructeur "Pure" qui apparait par
magie, il coincide *EXACTEMENT* avec la fonction "pure" de Applicative,
appliquée pour 'f' = 'Lift':

> instance Applicative (Lift f) where
> -- pure :: a -> Lift f a
>    pure x = Pure x
> ...

Maintenant, pour l'histoire d'erreurs. Le foncteur "Constant" c'est :

> data Constant a b = Constant { getConstant :: a }

Et son instance d'Applicative est un exercise assez simple et intéressant
selon ta familiarité avec ces derniers. Dans tous les cas, t'as bien
compris que le 'b' allait pas servir à grand chose.

Autrement dit, ça prend deux types en paramètre, ça ignore le deuxième et
stocke juste une valeur du premier type. Maintenant, Errors:

> type Errors e = Lift (Constant e)
> -- équivalent à
> type Errors e a = Lift (Constant e) a
> -- à son tour "moralement équivalent à" :
> data Errors e a = Pure a | Other (Constant e)

'e' étant un type représentant une erreur (comme String ou une liste de
String ou quoi).

Maintenant, les instances pour Lift devraient t'aider à comprendre, en te
disant que Errors rajoute aux valeurs "normales" (Pure, qui peut contenir
un truc de n'importe quel type!) des valeurs d'erreur. Donc si avait
t'avais des fonctions qui balançaient des erreurs à coups de 'error' et que
maintenant tu veux faire ça plus joliement, il te suffit d'utiliser
'pure'/'Pure' pour balancer tes valeurs normales dans un 'Errors e a', et
d'utiliser failure pour signaler une erreur. Essaye ensuite de voir comment
tout cela se passe si tu fixes 'e' et 'a' sur des types précis, [String] et
Int respectivement par exemple.

Une dernière chose: un des gros gros trucs sympas avec les applicatives
pour moi c'est de pouvoir écrire des trucs comme:

> data Person = Person { name :: String, age :: Int, ip :: IPV6 }
>
> Person <$> getNameFromNSALeak <*> getAgeFromFacebook <*> unsafeGetIp

où
 getNameFromNSALeak :: IO String
 getAgeFromFacebook :: IO Int
 unsafeGetIp :: IO IPV6


donc sont des trucs qui retournent des valeurs "dans un applicative" (ici
IO, mais aussi parsers, Maybe, conteneurs, Async, et bien d'autres).

N'hésite pas si certains passages ne sont pas clairs à demander des
éclaircissements.

-- 
Alp Mestanogullari
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-fr/attachments/20131221/81f49a57/attachment.html>


More information about the Haskell-fr mailing list