Идея о том, что стандартный класс 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-обозначения:
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-нотация работает только для 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
, но это не так, как в приведенном выше примере do-обозначения и Monad
.
Я буду использовать в основном Either
в качестве конструктора парных типов, а не конструктор типа (,)
, как в текущем классе Arrow
. Это может сделать код моей игрушечной игры RTS (cabal install DefendTheKind
) намного красивее.