haskellconstructormonadsoption-typelifting

Is there any way to "lift" a constructor?


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?


Solution

  • 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.