Является ли do-обозначение специфичным для "base: GHC.Base.Monad"? - PullRequest
9 голосов
/ 14 мая 2010

Идея о том, что стандартный класс 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) намного красивее.

1 Ответ

20 голосов
/ 14 мая 2010

Вам необходимо использовать расширение NoImplicitPrelude для полного повторного связывания синтаксиса, включая do и proc. В этом случае среди прочего вы получите следующее:

Нотация «Do» транслируется с использованием любых функций (>> =), (>>) и fail, которые находятся в области видимости (не версии Prelude). Понимания списка, mdo (раздел 7.3.6, «Рекурсивная запись») и параллельные массивы не затрагиваются.

Вы также можете настроить обработку отрицания, равенства, литеральных значений и еще много чего. Отличный способ запутать код!

p.s. - Если вы собираетесь перепривязать синтаксис do, то то, что sigfpe называет «параметризованными монадами» , очень весело. Та же идея доступна в category-extras в Control.Monad.Indexed . И да, они работают с повторяемым синтаксисом, несмотря на сильно отличающиеся сигнатуры типов!

...