6 Replies - 1425 Views - Last Post: 12 November 2014 - 07:56 AM

#1 cfoley   User is offline

  • Cabbage
  • member icon

Reputation: 2391
  • View blog
  • Posts: 5,023
  • Joined: 11-December 07

Haskell: Help sugaring monad

Posted 11 November 2014 - 10:00 AM

I'm being introduced to Haskell monads by the book Seven Languages in Seven Weeks. It uses an example of a monad that models a drunken pirate making a treasure map. Here it is:

module Main where
	data Position t = Position t deriving (Show)

	stagger (Position d) = Position (d + 2)
	crawl (Position d) = Position (d + 1)

	rtn x = x
	x >>== f = f x

	treasureMap pos = pos >>==
					stagger >>==
					stagger >>==
					crawl >>==
					rtn



The function treasureMap uses the monad without syntactic sugar. I think I understand it, and it's a lot less magical than I was expecting. >>== is an operator that lets me pipe functions together via currying. (Please correct me if I am wrong.)

Next the book explains there is the "do" notation that adds syntactic sugar to monads, and it gives an example using IO. This doesn't satisfy me because I can't see how the sugar relates to the core syntax. To help me get to the bottom of it, I'm trying to rewrite the treadureMap function to use the "do" syntax. This is the best I have come up with.

	treasureMap2 pos = do
		let { x = stagger pos } ;
		let { y = stagger x } ;
		let { z = crawl y } ;
		rtn z



This seems considerably worse than the first incarnation. In fact, it is considerably worse than the equivalent monad-free implementation:

	treasureMap3 pos = let x = stagger pos
	                       y = stagger x
	                       z = crawl y
	                   in z


Any help or advice would be very much appreciated.

Is This A Good Question/Topic? 0
  • +

Replies To: Haskell: Help sugaring monad

#2 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2740
  • View blog
  • Posts: 4,392
  • Joined: 21-June 11

Re: Haskell: Help sugaring monad

Posted 11 November 2014 - 04:48 PM

View Postcfoley, on 11 November 2014 - 06:00 PM, said:

The function treasureMap uses the monad without syntactic sugar. I think I understand it, and it's a lot less magical than I was expecting. >>== is an operator that lets me pipe functions together via currying. (Please correct me if I am wrong.)


There is no currying going on here - all functions involved are unary. But you're right that it's piping functions.

Quote

	treasureMap2 pos = do
		let { x = stagger pos } ;
		let { y = stagger x } ;
		let { z = crawl y } ;
		rtn z



The rules of do notation are as follows:

do {e}                   = e
do {e;stmts}             = e >> do {stmts}
do {p <- e; stmts}       = let ok p = do {stmts}
                               ok _ = fail "..."
                             in e >>= ok
do {let decls; stmts}    = let decls in do {stmts}



(Source: Haskell 2010 report)

As you can tell the shortened syntax for lets does not involve any monad operations - it is just a syntactic convenience (due to the fact that a do-block is, well, a block making the in redundant as it can be inferred from the indentation). So the only thing about your above code that's related to monads is your use of rtn/return, which isn't related to do notation.

If you want a do-block that corresponds to your use of >>=, you'll need to use the <- operator in the block like this:

do
  x <- pos
  y <- stagger p
  z <- stagger p2
  zz <- crawl
  return zz



This is also not any better than your monad-less version, but that's just because your monad is pointless.
Was This Post Helpful? 1
  • +
  • -

#3 cfoley   User is offline

  • Cabbage
  • member icon

Reputation: 2391
  • View blog
  • Posts: 5,023
  • Joined: 11-December 07

Re: Haskell: Help sugaring monad

Posted 11 November 2014 - 05:33 PM

Thanks for the reply. It's bed time for me so I'll have to read it carefully again tomorrow.

But

Quote

There is no currying going on here - all functions involved are unary. But you're right that it's piping functions.


Is this beast not binary?

x >>== f = f x

Was This Post Helpful? 0
  • +
  • -

#4 cfoley   User is offline

  • Cabbage
  • member icon

Reputation: 2391
  • View blog
  • Posts: 5,023
  • Joined: 11-December 07

Re: Haskell: Help sugaring monad

Posted 12 November 2014 - 12:50 AM

I tried your do block and it told me that p and p2 were not defined. So I changed it, and now it fails even more spectacularly!

	treasureMap4 pos = do
		x <- pos 
		y <- stagger x 
		z <- stagger y 
		zz <- crawl z 
		return zz


$ ghcii.sh drunken-monad.hs
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( drunken-monad.hs, interpreted )

drunken-monad.hs:28:17:
    Could not deduce (Monad Position) arising from a do statement
    from the context (Num (Position (Position B)/>),
                      Num (Position B)/>,
                      Num B)/>
      bound by the inferred type of
               treasureMap4 :: (Num (Position (Position B)/>), Num (Position B)/>,
                                Num B)/> =>
                               Position (Position (Position (Position B)/>)) -> Po
sition b
      at drunken-monad.hs:(27,9)-(32,25)
    Possible fix: add an instance declaration for (Monad Position)
    In a stmt of a 'do' block: x <- pos
    In the expression:
      do { x <- pos;
           y <- stagger x;
           z <- stagger y;
           zz <- crawl z;
           .... }
    In an equation for `treasureMap4':
        treasureMap4 pos
          = do { x <- pos;
                 y <- stagger x;
                 z <- stagger y;
                 .... }
Failed, modules loaded: none.
Prelude>


My whole code looks like this. Please excuse the commented out code.

module Main where
	data Position t = Position t deriving (Show)

	stagger (Position d) = Position (d + 2)
	crawl (Position d) = Position (d + 1)

	rtn x = x
	x >>== f = f x

	--treasureMap pos = pos >>==
	--				stagger >>==
	--				stagger >>==
	--				crawl >>==
	--				rtn

	--treasureMap2 pos = do
	--	let { x = stagger pos } ;
	--	let { y = stagger x } ;
	--	let { z = crawl y } ;
	--	rtn z

	--treasureMap3 pos = let x = stagger pos
	--                       y = stagger x
	--                       z = crawl y
	--                   in z

	treasureMap4 pos = do
		x <- pos 
		y <- stagger x 
		z <- stagger y 
		zz <- crawl z 
		return zz

Was This Post Helpful? 0
  • +
  • -

#5 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2740
  • View blog
  • Posts: 4,392
  • Joined: 21-June 11

Re: Haskell: Help sugaring monad

Posted 12 November 2014 - 07:23 AM

View Postcfoley, on 12 November 2014 - 01:33 AM, said:

Quote

There is no currying going on here - all functions involved are unary. But you're right that it's piping functions.


Is this beast not binary?

x >>== f = f x



Yes, it is. And technically it is curried because all infix operators in Haskell are curried, but I did not think that that was what you were referring to. When I said everything was unary, I was referring to stagger and crawl.

Anyway the fact that >>== is a curried function is not relevant to the example.


View Postcfoley, on 12 November 2014 - 08:50 AM, said:

I tried your do block and it told me that p and p2 were not defined.


Oops. That was me being inattentive when renaming my variables. The reply box really needs refactoring support ;-)

Quote

So I changed it, and now it fails even more spectacularly!


Yes, that's because your monad isn't a real monad and do requires a real monad. For a real monad you need >>= and return instead of >>== and rtn and you need to define it as an instance. You also need to define a wrapper type that should be the monad for type m a = a (nor could you define such a type alias in the first place).

I assume your book used this "pseudo monad" to introduce the concept of monads without first having to explain type classes. In order to actually use do notation, you need an actual monad implementing the monad type class. I would hope that the book mentions this.

This post has been edited by sepp2k: 12 November 2014 - 07:23 AM

Was This Post Helpful? 1
  • +
  • -

#6 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2740
  • View blog
  • Posts: 4,392
  • Joined: 21-June 11

Re: Haskell: Help sugaring monad

Posted 12 November 2014 - 07:45 AM

Here's how your code looks like using real monads:

data Position t = Position t deriving (Show)

instance Monad Position
    return x = Position x
    (MyMonad x) >>= f = f x

-- To use stagger and crawl as the right operand to >>=,
-- they actually need to take a t, not a Position
stagger d = return (d + 2)
crawl d = return (d + 1)


-- Without do notation
treasureMap pos =
    stagger pos >>=
    stagger >>=
    crawl >>=
    return

-- With do
treasureMapDo pos = do
    x <- stagger pos
    y <- stagger x
    z <- crawl y
    return z

-- Or with less redundancy
-- Without do notation
treasureMap2 pos =
    stagger pos >>=
    stagger >>=
    crawl

-- With do
treasureMap2Do pos = do
    x <- stagger pos
    y <- stagger x
    crawl y


Was This Post Helpful? 1
  • +
  • -

#7 cfoley   User is offline

  • Cabbage
  • member icon

Reputation: 2391
  • View blog
  • Posts: 5,023
  • Joined: 11-December 07

Re: Haskell: Help sugaring monad

Posted 12 November 2014 - 07:56 AM

Thanks very much for your helpful replies. You're right that the book did not introduce type classes and the reply box definitely does need refactoring support.

I now have an idea of how the do syntax relates to the unsugared syntax and I have a little idea of what a real monad is.

Thanks again!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1