标准Monad
类存在缺陷并且实际应该扩展Functor
或Pointed
的想法正在浮动。
我不一定声称这是正确的做法,但假设有人试图这样做:
import Prelude hiding (Monad(..))
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
join = (>>= id)
(>>=) :: m a -> (a -> m b) -> m b
a >>= t = join (fmap t a)
(>>) :: m a -> m b -> m b
a >> b = a >>= const b
到目前为止一直很好,但是在尝试使用do-notation时:
whileM :: Monad m => m Bool -> m ()
whileM iteration = do
done <- iteration
if done
then return ()
else whileM iteration
编译器抱怨:
Could not deduce (base:GHC.Base.Monad m) from the context (Monad m)
问题:
do-notation是否仅适用于base:GHC.Base.Monad
?有没有办法让它与另一个Monad
类一起工作?
额外背景:
我真正想做的是将base:Control.Arrow.Arrow
替换为“通用”Arrow
类:
{-# LANGUAGE TypeFamilies #-}
class Category a => Arrow a where
type Pair a :: * -> * -> *
arr :: (b -> c) -> a b c
first :: a b c -> a (Pair a b d) (Pair a c d)
second :: a b c -> a (Pair a d b) (Pair a d c)
(***) :: a b c -> a b' c' -> a (Pair a b b') (Pair a c c')
(&&&) :: a b c -> a b c' -> a b (Pair a c c')
然后在我的Arrow
类中使用Arrow
的proc-notation,但是在上面的do-notation和Monad
示例中失败了。
我主要使用Either
作为我的对类型构造函数,而不是(,)
类型构造函数,与当前Arrow
类一样。这可能会让我的玩具RTS游戏(cabal install DefendTheKind
)的代码变得更漂亮。
答案 0 :(得分:20)
您需要使用NoImplicitPrelude extension获取完全可重新绑定的语法,包括do
和proc
。在这种情况下,您可以获得以下内容:
“Do”符号使用任何函数(&gt;&gt; =),(&gt;&gt;)和失败进行翻译,都在范围内(不是Prelude版本)。列表推导,mdo(第7.3.6节“递归标记”)和并行数组推导不受影响。
你也可以调整一些否定,平等,文字值和诸如此类的处理。混淆代码的好方法!
P.S。 - 如果您要重新绑定do
语法,what sigfpe calls "parameterized monads"非常有趣。 category-extras
下的{{1}}提供了同样的想法。是的,他们确实使用可重新绑定的语法,尽管类型签名截然不同!