0

Assume I have a function (plusOne in the example below) that only accepts and returns an Int. But I don't have an Int; instead, I have a Maybe Int; and if this Maybe Int contains a value then I want to pass it to plusOne and get a Just of whatever plusOne returns, or if its rather a Nothing then I want this Nothing to propagate.

In such a case we have liftM to code this in an elegant way:

import Control.Monad

plusOne :: Int -> Int
plusOne n =
    n+1 -- a very complicated computation that is failsafe

main =
    let n = Just 15 -- a very complicated computation that can fail
    in let res = liftM plusOne n
    in print res

So far so good. But, can something like this be done with constructors as well?

Forget about plusOne. Now I have: data SomeData = SomeData Int and want to obtain a Maybe (SomeData Int) from my Maybe Int. The solution seems noticeably less elegant:

import Control.Monad

data SomeData = SomeData Int
    deriving Show -- so that print works

main =
    let n = Just 15
    in let res = n >>= (\nn -> Just (SomeData nn))
    -- alternatively: in let res = liftM (\nn -> SomeData nn) n
    in print res

Both solutions above (with >>= or with liftM) require going through an anonymous lambda function which, to my intuition, shouldn't be necessary and only serves to clobber the code. Is there a way to avoid it? Can I somehow "lift" the someData constructor just as I could lift plusOne in the first snippet?

1 Answers1

3

You can simply use function composition:

main =
    let n = Just 15
        res = n >>= Just . SomeData
    in print res

although as Robin Zigmond points out, you can simply use fmap, since fmap f x is equivalent to x >>= return . f, and return == Just for the Maybe monad.

main = let n = Just 15
           res = fmap SomeData n  -- or SomeData <$> n
       in print res

Moral of the story: don't use a monad where all you need is a functor.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • So now you prompted me to check what the hay `fmap` is :) Dang I know and understand very few about Haskell and yet the esteemed Academy wants me to do quite a large homework involving monads after no more than two months of introduction to this language. –  Mar 20 '19 at 13:30
  • I also don't know what functors are ;P even though this was skimmed over during one of the lectures, too bad I didn't pay too much attention. I'm just starting to understand the very basics of monads, that's where I am. –  Mar 20 '19 at 13:32
  • 1
    Either you aren't paying attention in class, or your class is doing things *extremely* backwards. You should have learned functors *long* before being introduced to monads (since every monad *is* a functor, both by mathematical definition and in Haskell, since a `Functor` instance is required for every type defining a `Monad` instance). – chepner Mar 20 '19 at 13:32
  • Perhaps both. Please, there's *half a semester* for Haskell. I feel this is too short of a time. I feel kind of overwhelmed by lots and lots of new concepts introduced in a very short time. –  Mar 20 '19 at 13:36
  • 2
    totally agree with @chepner that the way you seem to be learning is backwards. If you need a relatively quick and pain-free introduction to these concepts, I would strongly recommend [Learn You a Haskell](http://learnyouahaskell.com/chapters) - especially in this context chapters 11 and 12. – Robin Zigmond Mar 20 '19 at 13:37
  • 4
    @gaazkam `fmap` and `liftM` are exactly the same, except that `fmap` works on a (very) few more types than `liftM` does. – Daniel Wagner Mar 20 '19 at 15:30
  • @gaazkam I agree with you on a half semester being too short. Learning functional programming, especially with algebraic types, polymorphism, typeclasses, functors, monads, etc. requires more time. – chi Mar 20 '19 at 21:08