[suivi de Gabriel Gonzalez répondre]
Le droit de notation pour les contraintes et les quantifications en Haskell est la suivante:
<functions-definition> ::= <functions> :: <quantified-type-expression>
<quantified-type-expression> ::= forall <type-variables-with-kinds> . (<constraints>) => <type-expression>
<type-expression> ::= <type-expression> -> <quantified-type-expression>
| ...
...
Les genres peuvent être omis, ainsi que forall
s pour le rang-1 types:
<simply-quantified-type-expression> ::= (<constraints-that-uses-rank-1-type-variables>) => <type-expression>
Par exemple:
{-# LANGUAGE Rank2Types #-}
msum :: forall m a. Monoid (m a) => [m a] -> m a
msum = mconcat
mfilter :: forall m a. (Monad m, Monoid (m a)) => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mempty }
guard :: forall m. (Monad m, Monoid (m ())) => Bool -> m ()
guard True = return ()
guard False = mempty
ou sans Rank2Types
(puisque nous n'avons que de rang 1 types ici), et à l'aide de CPP
(j4f):
{-# LANGUAGE CPP #-}
#define MonadPlus(m, a) (Monad m, Monoid (m a))
msum :: MonadPlus(m, a) => [m a] -> m a
msum = mconcat
mfilter :: MonadPlus(m, a) => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mempty }
guard :: MonadPlus(m, ()) => Bool -> m ()
guard True = return ()
guard False = mempty
Le "problème" est que nous ne pouvons pas écrire
class (Monad m, Monoid (m a)) => MonadPlus m where
...
ou
class forall m a. (Monad m, Monoid (m a)) => MonadPlus m where
...
C'est, forall m a. (Monad m, Monoid (m a))
peut être utilisé comme une application autonome contrainte, mais ne peut pas être lissés avec un nouveau-paramétrique typeclass pour *->*
types.
C'est parce que le typeclass définition mécanisme fonctionne comme ceci:
class (constraints[a, b, c, d, e, ...]) => ClassName (a b c) (d e) ...
c'est à dire le membre de droite côté d'introduire des variables de type, pas de gauche ou forall
à gauche.
Au lieu de cela, nous avons besoin d'écrire 2-paramétrique typeclass:
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
class (Monad m, Monoid (m a)) => MonadPlus m a where
mzero :: m a
mzero = mempty
mplus :: m a -> m a -> m a
mplus = mappend
instance MonadPlus [] a
instance Monoid a => MonadPlus Maybe a
msum :: MonadPlus m a => [m a] -> m a
msum = mconcat
mfilter :: MonadPlus m a => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mzero }
guard :: MonadPlus m () => Bool -> m ()
guard True = return ()
guard False = mzero
Inconvénients: nous avons besoin de spécifier deuxième paramètre à chaque fois que nous utilisons MonadPlus
.
Question: comment
instance Monoid a => MonadPlus Maybe a
peut être écrit si MonadPlus
est un paramétriques typeclass? MonadPlus Maybe
de base
:
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` ys = ys
xs `mplus` _ys = xs
fonctionne pas comme Monoid Maybe
:
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2) -- < here
:
(Just [1,2] `mplus` Just [3,4]) `mplus` Just [5,6] => Just [1,2]
(Just [1,2] `mappend` Just [3,4]) `mappend` Just [5,6] => Just [1,2,3,4,5,6]
Analogiquement, forall m a b n c d e. (Foo (m a b), Bar (n c d) e)
donne lieu, pour (7 - 2 * 2)-paramétrique typeclass si nous voulons *
types, (7 - 2 * 1)-paramétrique typeclass pour * -> *
types, et (7 - 2 * 0) pour * -> * -> *
types.