Haskell do notation to bind
Asked Answered
S

3

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

Shantell answered 6/6, 2013 at 14:27 Comment(0)
A
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
Archimandrite answered 6/6, 2013 at 14:44 Comment(1)
Thank for pushing my head against this wall. Don´t know why, but I just didn´t of this simple solution.Shantell
B
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.

Braca answered 6/6, 2013 at 14:50 Comment(1)
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 =)Shantell
G
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.

Grantley answered 6/6, 2013 at 15:13 Comment(3)
Thanks for this continuative explanation =)Shantell
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.Kosciusko
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.Kosciusko

© 2022 - 2024 — McMap. All rights reserved.