Question

I´am trying to desugar a do statement in Haskell. I have found some examples here on SO but wasn´t able to apply them to my case. Only thing I can think of is a heavy nested let statement, which seems quite ugly.

Statement in which do notation should be replaced by bind:

do num <- numberNode x
   nt1 <- numberTree t1
   nt2 <- numberTree t2
   return (Node num nt1 nt2)

Any input is highly appreciated =)

Was it helpful?

Solution

numberNode x >>= \num ->
  numberTree t1 >>= \nt1 ->
    numberTree t2 >>= \nt2 ->
      return (Node num nt1 nt2)

Note that this is simpler if you use Applicatives:

Node <$> numberNode x <*> numberTree t1 <*> numberTree t2

OTHER TIPS

This is an excellent use case for applicative style. You can replace your entire snippet (after importing Control.Applicative) with

Node <$> numberNode x <*> numberTree t1 <*> numberTree t2

Think of the applicative style (using <$> and <*>) as "lifting" function application so it works on functors as well. If you mentally ignore <$> and <*> it looks quite a lot like normal function application!

Applicative style is useful whenever you have a pure function and you want to give it impure arguments (or any functor arguments, really) -- basically when you want to do what you specified in your question!


The type signature of <$> is

(<$>) :: Functor f => (a -> b) -> f a -> f b

which means it takes a pure function (in this case Node) and a functor value (in this case numberNode x) and it creates a new function wrapped "inside" a functor. You can add further arguments to this function with <*>, which has the type signature

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

As you can see, this is very similar to <$> only it works even when the function is wrapped "inside" a functor.

I'd like to add to the posts about Applicative above..

Considering the type of <$>:

(<$>) :: Functor f => (a -> b) -> f a -> f b

it looks just like fmap:

fmap :: Functor f => (a -> b) -> f a -> f b

which is also very much like Control.Monad.liftM:

liftM :: Monad m => (a -> b) -> m a -> m b

I think of this as "I need to lift the data constructor into this type"

On a related note, if you find yourself doing this:

action >>= return . f

you can instead do this:

f `fmap` action

The first example is using bind to take the value out of whatever type action is, calling f with it, and then repacking the result. Instead, we can lift f so that it takes the type of action as its argument.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top