[加布里埃尔冈萨雷斯回答的后续]
Haskell 中约束和量化的正确表示法如下:
<functions-definition> ::= <functions> :: <quantified-type-expression>
<quantified-type-expression> ::= forall <type-variables-with-kinds> . (<constraints>) => <type-expression>
<type-expression> ::= <type-expression> -> <quantified-type-expression>
| ...
...
kinds 可以省略,forall
rank-1 类型的 s 也可以省略:
<simply-quantified-type-expression> ::= (<constraints-that-uses-rank-1-type-variables>) => <type-expression>
例如:
{-# 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
或没有Rank2Types
(因为我们这里只有 rank-1 类型),并使用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
“问题”是我们不能写
class (Monad m, Monoid (m a)) => MonadPlus m where
...
或者
class forall m a. (Monad m, Monoid (m a)) => MonadPlus m where
...
也就是说,forall m a. (Monad m, Monoid (m a))
可以用作独立约束,但不能使用新的类型的单参数类型类来别名*->*
。
这是因为类型类定义机制是这样工作的:
class (constraints[a, b, c, d, e, ...]) => ClassName (a b c) (d e) ...
即rhs端引入类型变量,而不是lhs 或forall
lhs。
相反,我们需要编写 2 参数类型类:
{-# 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
缺点:我们每次使用时都需要指定第二个参数MonadPlus
。
问题:如何
instance Monoid a => MonadPlus Maybe a
MonadPlus
如果是单参数类型类可以写吗?MonadPlus Maybe
来自base
:
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` ys = ys
xs `mplus` _ys = xs
不像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]
类似地,forall m a b n c d e. (Foo (m a b), Bar (n c d) e)
如果我们想要*
类型,则产生 (7 - 2 * 2)-parametric typeclass,类型产生 (7 - 2 * 1)-parametric typeclass,* -> *
类型产生 (7 - 2 * 0) * -> * -> *
。