views:

125

answers:

1

The idea that the standard Monad class is flawed and that it should actually extend Functor or Pointed is floating around.

I'm not necessarily claiming that it is the right thing to do, but suppose that one was trying to do it:

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

So far so good, but then when trying to use do-notation:

whileM :: Monad m => m Bool -> m ()
whileM iteration = do
    done <- iteration
    if done
        then return ()
        else whileM iteration

The compiler complains:

Could not deduce (base:GHC.Base.Monad m) from the context (Monad m)

Question:

Does do-notation work only for base:GHC.Base.Monad? Is there a way to make it work with an alternative Monad class?

Extra context:

What I really want to do is replace base:Control.Arrow.Arrow with a "generalized" Arrow class:

{-# 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')

And then use the Arrow's proc-notation with my Arrow class, but that fails like in the example above of do-notation and Monad.

I'll use mostly Either as my pair type constructor and not the (,) type constructor as with the current Arrow class. This might allow to make the code of my toy RTS game (cabal install DefendTheKind) much prettier.

+14  A: 

You need to use the NoImplicitPrelude extension for full rebindable syntax, including do and proc. In that case, you get the following, among other stuff:

"Do" notation is translated using whatever functions (>>=), (>>), and fail, are in scope (not the Prelude versions). List comprehensions, mdo (Section 7.3.6, “The recursive do-notation ”), and parallel array comprehensions, are unaffected.

You can also tweak some handling of negation, equality, literal values, and whatnot. Great way to obfuscate code!

p.s. -- If you're going to rebind the do syntax, what sigfpe calls "parameterized monads" are great fun. The same idea is available in category-extras under Control.Monad.Indexed. And yes, they do work with rebindable syntax, despite the wildly different type signatures!

camccann
@camccann: awesome, thanks! :) but also, arg! :( too bad that for `Arrow` - "unlike the other constructs, the types of these functions must match the Prelude types very closely", I wonder why is that..
yairchu
@yairchu: I imagine it's at least partly due to arrows being less frequently used and the `proc` syntax itself being a more recent addition. As heroic as the GHC team may be, they can't just wave a magic wand to implement every feature that gets thought up...
camccann
@camccann: The heroism of the GHC team is unparalleled! In fact - they were heroic enough to include Template Haskell! I think I'll try using to make a work-around for this. One will define a "base" Arrow using proc and then use a convertor TH function to make a "generalized" arrow out of it.
yairchu