tags:

views:

256

answers:

3

I want to create my own monad. This is what i wrote:

data LeafConType a = LeafCon (a,Int,Int)

instance Monad (LeafConType ) where
return = LeafCon 
lc@(LeafCon (t,i,n)) >>= f = if i>=n
                                then lc
                                else f (t,i,n)

But this dont work. Ghc says:

leafcon.hs:26:1:
    Occurs check: cannot construct the infinite type: a = (a, Int, Int)
    When generalising the type(s) for `return'
    In the instance declaration for `Monad LeafConType'

leafcon.hs:27:1:
    Occurs check: cannot construct the infinite type: a = (a, Int, Int)
    When generalising the type(s) for `>>='
    In the instance declaration for `Monad LeafConType'

Whats wrong with that?


I want to do calculations while i is lower than n. n should be constants by I don't know yet how to do this correct. It should be some mix of State and Maybe. If you have some advices feel free to share it with me:P

+4  A: 

The functions you specified for >>= and return don't satisfy the types required by Monad:

return :: a -> LeafConType a

Given the declaration

return = LeafCon

you give the function the incompatible type

return :: (a, Int, Int) -> LeafConType a

A statement like return 42 would therefore be impossible in your monad.

I don't understand what your monad should do at all. First take a look at simple, working monads!

instance Monad [] where
    (>>=) = concatMap
    return a = [a]

instance Monad Maybe where
    return = Just
    (Just x) >>= f = f x
    Nothing >>= f = Nothing
Dario
I should have pressed that button 10 seconds sooner... ;)
Stephan202
+6  A: 

About return:

Prelude> :t return
return :: (Monad m) => a -> m a

So return takes an argument of type a, and returns something of type m a. In this case m is LeafConType, so LeafConType a is returned.

Now suppose that we pass True. Then a = Bool, so the return type must be LeafConType Bool. However, you define:

return = LeafCon

So, return True becomes LeafCon True. But that is not allowed, because the type definition of LeafConType states that

data LeafConType a = LeafCon (a, Int, Int)

So for LeafConType Bool the argument to LeafCon must have type (Bool, Int, Int), not just Bool. And that is what the compile error means: a cannot be the same as (a, Int, Int). You state:

I want to do calculations while i is lower than n.

This means that you will need some default values for i and n, for otherwise it will be impossible to define return. If both of them are zero by default, then you could define:

return a = LeafCon (a, 0, 0)

About (>>=):

Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

Now look at your implementation (slightly different notation, same idea):

lc@(LeafCon (t, i, n)) >>= f | i >= n    = lc 
                             | otherwise = f t

What we see here, is that lc is returned when i >= n. But lc is of type LeafConType a, while f is a function which may return a value of type LeafConType b, for any b. As a result it could be that b is not equal to a and hence these types don't match. In conclusion, you seriously have to ask yourself one question:

  Can this type of computation be expressed as a monad anyway?

Stephan202
I think it can, maybe not this way, but its possible. It should carry information like State monad, and end calculations like Maybe monad.
qba
Have you considered using a state transformer? http://en.wikibooks.org/wiki/Haskell/Monad_transformers. Have a look at `StateT` and `ErrorT`.
Stephan202
and don't forget the `MaybeT`
barkmadley
A: 

Judging from your description of what you want your monad to do, I think you want something a bit like this:

data LeafConType a = LeafCon { runLeafCon' :: Int -> Int -> (Maybe a, Int, Int) }

runLeafCon :: Int -> Int -> LeafConType a -> Maybe a
runLeafCon i n lc = let (t, _, _) = runLeafCon' lc i n in t

getI :: LeafConType Int
getI = LeafCon $ \i n -> (Just i, i, n)

getN :: LeafConType Int
getN = LeafCon $ \i n -> (Just n, i, n)

setI :: Int -> LeafConType ()
setI i = LeafCon $ \_ n -> (Just (), i, n)

setN :: Int -> LeafConType ()
setN n = LeafCon $ \i _ -> (Just (), i, n)

instance Monad LeafConType where
    return t = LeafCon $ \i n -> if (i < n) 
                                 then (Just t, i, n) 
                                 else (Nothing, i, n)

    (LeafCon k) >>= f = 
        LeafCon $ \i n -> 
            let (t, i', n') = k i n
            in case t of
                 Nothing -> (Nothing, i', n')
                 (Just t') -> if (i' < n')
                              then runLeafCon' (f t') i' n'
                              else (Nothing, i, n)


example :: Int -> LeafConType ((), Int)
example x = do 
  i <- getI
  m <- setI (i + x)
  return (m, i + x)

Some examples:

*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)

I threw this together pretty quickly, it's rather ugly, and I haven't checked to see whether it obeys any of the Monad laws, so use at your peril! :)

Alasdair