NeuronQ
NeuronQ

Reputation: 8195

Haskell: how to get the types right when coding your own "bind" for better understanding?

I'm trying to wrap my head around Haskell's monads by actually trying to manually implement some of the used operators, like >>= (bind). I just do this to try and get a better understanding of things, there is no practical use in this endeavor. But I get to a brick wall from the get go because code that appears like "code examples" in explanations of monads turn out not to be actually compilable code...

When I try to define my own bind I get a type error. This code:

bind :: IO a -> (a -> IO b) -> IO b
(bind action1 action2) world0 =
    let (a, world1) = action1 world0
        (b, world2) = action2 a world1
    in
        (b, world2)

...gives me these (9 is the first line of the code above):

iom.hs:10:1:
    Couldn't match expected type `t0 -> (t1, t2)'
                with actual type `IO b'
    The equation(s) for `bind' have three arguments,
    but its type `IO a -> (a -> IO b) -> IO b' has only two

iom.hs:11:23:
    Couldn't match expected type `t0 -> (t1, t2)'
                with actual type `IO a'
    The function `action1' is applied to one argument,
    but its type `IO a' has none
    In the expression: action1 world0
    In a pattern binding: (a, world1) = action1 world0

iom.hs:12:23:
    Couldn't match expected type `t2 -> (t0, t1)'
                with actual type `IO b'
    The function `action2' is applied to two arguments,
    but its type `a -> IO b' has only one
    In the expression: action2 a world1
    In a pattern binding: (b, world2) = action2 a world1

I am just starting with an example from http://www.haskell.org/haskellwiki/IO_inside , just replacing >>= with bind to prevent a name clash and defining it as a function not infox operator. This is the example provided as an explanation (copy pasted):

(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
   let (a, world1) = action1 world0
       (b, world2) = action2 a world1
   in (b, world2)

Now if I replace the last line with return (b, world2) I get this instead:

iom.hs:10:1:
    Couldn't match expected type `t0 -> m0 (t1, t2)'
                with actual type `IO b'
    The equation(s) for `bind' have three arguments,
    but its type `IO a -> (a -> IO b) -> IO b' has only two

iom.hs:11:23:
    Couldn't match expected type `t0 -> (t1, t2)'
                with actual type `IO a'
    The function `action1' is applied to one argument,
    but its type `IO a' has none
    In the expression: action1 world0
    In a pattern binding: (a, world1) = action1 world0

iom.hs:12:23:
    Couldn't match expected type `t2 -> (t0, t1)'
                with actual type `IO b'
    The function `action2' is applied to two arguments,
    but its type `a -> IO b' has only one
    In the expression: action2 a world1
    In a pattern binding: (b, world2) = action2 a world1

Upvotes: 1

Views: 285

Answers (2)

duplode
duplode

Reputation: 34378

That happens because the IO inside tutorial uses a fictional (though inspired by the real thing) definition for IO (see section 3 for details):

type IO a  =  RealWorld -> (a, RealWorld)

Using this definition, IO a is actually a synonym for a function type, and so the extra argument your error messages refer to wouldn't be a problem. The actual IO type is abstract, so you can't see or use in your code what it is actually made of.

If you want to run the article code yourself, use that definition while renaming IO to e.g. MyIO and change your other definitions accordingly.

Upvotes: 6

David Young
David Young

Reputation: 10783

As they say in the article, that definition of IO is actually not correct. IO is not a type synonym for a function type, it's a bit more mysterious than that. The code given only works if IO is a type synonym for a function because, if not, you are trying to specify three arguments for a function whose signature says it takes two arguments.

There is a Monad that works much more like what they are describing called State.

Upvotes: 3

Related Questions