Alternative
有些不太正规。它本质上是
单子构造器的类:类型构造器
T
,使得对于任何包含类型
X
,
T X
是一个单子。这与函子、单子无关,数学上也没有那么深奥。(因此,仅出于数学优雅性考虑,将
Monad
设置在
Alternative
下面可能有点糟糕。)
让我们以
Monoid
为例来清楚地写出该实例(这实际上不会编译):
instance (Foldable f, (∀ x . Monoid (f x))) => Monad f where
(>>=) = flip $ \f -> foldr mappend empty . fmap f
≡ flip $ \f -> fold . fmap f
≡ flip foldMap
或者说
(=<<) = foldMap
所以,这绝对不是什么未知的事情。
要检查法律,我们最好看一下Kleisli公式:
(f <=< g) x = f =<< g x
≡ foldMap f $ g x
即,也就是说。
f <=< g = foldMap f . g
然后单子定律如下:
Left identity
f <=< pure ≡ foldMap f . pure =! f
Right identity
pure <=< f ≡ foldMap pure . f =! f
Associativity
(f <=< g) <=< h ≡ foldMap (foldMap f . g) . h
=! foldMap f . foldMap g . h
≡ foldMap f . (foldMap g . h) ≡ f <=< (g <=< h)
简而言之,我们需要:
foldMap f . pure =! f =! foldMap pure . f
∀ f
foldMap (foldMap f . g) =! foldMap f . foldMap g
∀ f
,g
这看起来似乎不令人过分担心,但我并不认为你可以严格地推导出任意+实例的结果。
实际上,我认为这个实例的最大问题在于它远远没有足够的普适性。大多数monad既不是也不是。如果有一个像你所提议的那样的万能定义,它将需要使用才能定义自己的任何实例,而通常认为不应该没有充分理由就使用这些实例。
然而,我确实想知道下面的默认定义bind方法是否存在任何问题:
{-# LANGUAGE DefaultSignatures #-}
class Applicative f => Monad f where
return :: a -> m a
return = pure
(>>=) :: m a -> (a -> m b) -> m b
default (>>=) :: (Foldable m, Monoid m b)
=> m a -> (a -> m b) -> m b
(>>=) = flip foldMap
这样至少允许将列表实例简单定义为:
instance Monad []
不需要写出方法,因为foldMap ≡ concatMap ≡ (=<<)
。
(注:该句话是关于it技术的内容,涉及函数式编程中的方法调用。)