问题
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 =)
回答1:
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
回答2:
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.
回答3:
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.
来源:https://stackoverflow.com/questions/16964732/haskell-do-notation-to-bind