13

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 =)

hammar
  • 138,522
  • 17
  • 304
  • 385
floAr
  • 799
  • 1
  • 6
  • 29

3 Answers3

16
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
luqui
  • 59,485
  • 12
  • 145
  • 204
Gabriella Gonzalez
  • 34,863
  • 3
  • 77
  • 135
  • Thank for pushing my head against this wall. Don´t know why, but I just didn´t of this simple solution. – floAr Jun 06 '13 at 15:40
8

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.

kqr
  • 14,791
  • 3
  • 41
  • 72
  • Thank you for mentioning this, I have no experience with applicatives but the concept looks awesome. It is just this kind of beautifull ways to solve problems I like about Haskell :) As I was asking for bind operator I will accept Gabriels answer, but I will definitely take a deeper look at this concept. Thank you very much for pointing this out to me =) – floAr Jun 06 '13 at 15:38
2

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.

dino
  • 1,123
  • 9
  • 13
  • Thanks for this continuative explanation =) – floAr Jun 06 '13 at 15:39
  • 1
    `liftM3 :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r` or `liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d` are available by importing `Control.Monad` or `Control.Applicative`, and are more useful than plain `fmap`/`liftM` here. For example, `liftA3 Node (numberNode x) (numberTree t1) (numberTree t2)` would do the trick. – AndrewC Jun 06 '13 at 16:06
  • As it stands, this doesn't actully answer the question. The OP can't use it to solve the problem they have. That's why you need liftM3 or Applicative. – AndrewC Jun 09 '13 at 07:26