Pour définir une monade pour (->) r
, nous avons besoin de deux opérations, return
et (>>=)
, sous réserve de trois lois:
instance Monad ((->) r) where
Si l'on regarde le signature de rendement pour l' (->) r
return :: a -> r -> a
nous pouvons voir son juste la fonction constante, qui ignore son deuxième argument.
return a r = a
Ou alternativement,
return = const
Pour construire (>>=)
, si nous nous spécialisons son type de signature avec la monade (->) r
,
(>>=) :: (r -> a) -> (a -> r -> b) -> r -> b
il n'y a vraiment qu'une seule définition possible.
(>>=) x y z = y (x z) z
À l'aide de cette monade est comme le passage de l'un argument supplémentaire r
pour chaque fonction. Vous pouvez l'utiliser pour la configuration, ou pour passer des options en profondeur dans les entrailles de votre programme.
On peut vérifier que c'est une monade, en vérifiant les trois monade lois:
1. return a >>= f = f a
return a >>= f
= (\b -> a) >>= f -- by definition of return
= (\x y z -> y (x z) z) (\b -> a) f -- by definition of (>>=)
= (\y z -> y ((\b -> a) z) z) f -- beta reduction
= (\z -> f ((\b -> a) z) z) -- beta reduction
= (\z -> f a z) -- beta reduction
= f a -- eta reduction
2. m >>= return = m
m >>= return
= (\x y z -> y (x z) z) m return -- definition of (>>=)
= (\y z -> y (m z) z) return -- beta reduction
= (\z -> return (m z) z) -- beta reduction
= (\z -> const (m z) z) -- definition of return
= (\z -> m z) -- definition of const
= m -- eta reduction
La finale de l'errance de la loi:
3. (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
suit par de semblables, facile général paramétré par le raisonnement.
On peut définir un certain nombre d'autres classes pour ((->) r) ainsi, comme Foncteur,
instance Functor ((->) r) where
et si l'on regarde le signature de
-- fmap :: (a -> b) -> (r -> a) -> r -> b
nous pouvons voir que sa viens de la composition!
fmap = (.)
De même, nous pouvons faire une instance d' Applicative
instance Applicative ((->) r) where
-- pure :: a -> r -> a
pure = const
-- (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
(<*>) g f r = g r (f r)
Ce qui est sympa sur le fait d'avoir ces cas, c'est qu'ils vous permettent d'employer tous les Monade et Applicative combinators lors de la manipulation de fonctions.
Il y a beaucoup d'instances de classes impliquant (->), par exemple, vous pourriez vous inscrire à la main l'instance de Monoïde (b -> a), compte tenu d'un Monoïde sur a
comme:
enter code here
instance Monoid a => Monoid (b -> a) where
-- mempty :: Monoid a => b -> a
mempty _ = mempty
-- mappend :: Monoid a => (b -> a) -> (b -> a) -> b -> a
mappend f g b = f b `mappend` g b
mais compte tenu de la Monade/Applicative exemple, vous pouvez également définir cette instance avec
instance Monoid a => Monoid (r -> a) where
mempty = pure mempty
mappend = liftA2 mappend
à l'aide de l'Applicatif exemple pour (->) r
ou avec
instance Monoid a => Monoid (r -> a) where
mempty = return mempty
mappend = liftM2 mappend
à l'aide de la Monade exemple pour (->) r
.
Ici, les économies sont minimes, mais, par exemple, l' @pl outil de génération de point de code libre, qui est fourni par lambdabot sur le #haskell canal IRC abus de ces cas tout à fait un peu.