Apply a constructor in a do context in Haskell

Viewed 54

Given the following declarations

data MyCustomString = MyCustomString String deriving (Show, Eq)

getSomeString :: IO String

I'd like to process the output of getSomeString into a IO MyCustomString in a do context:

do
    cs_one <- getSomeString
    cs_two <- getSomeString

    -- at this point, `cs_one` and `cs_two` are of type `String`
    -- how to construct them into `MyCustomString` instead with no additional lines?

However, getSomeString returns an String and not an MyCustomString. How could I use the MyCustomString constructor so that both cs_one and cs_two are, at this point in the code, of type MyCustomString ?

I've tried this to no avail:

-- this does not work
do
    MyCustomString cs_one <- getSomeString
    MyCustomString cs_two <- getSomeString

-- this neither
do
    cs_one <- MyCustomString getSomeString
    cs_two <- MyCustomString getSomeString
2 Answers

The usual way is:

do
    cs_one <- MyCustomString <$> getSomeString
    cs_two <- MyCustomString <$> getSomeString

The <$> operator is a synonym for fmap and has the signature:

(<$>) :: Functor f => (a -> b) -> f a -> f b

Since monads are functors, it specializes to the monad IO as:

(<$>) :: (a -> b) -> IO a -> IO b

which applies a function "under" the IO monad, in this case turning your IO String into an IO MyCustomString.

Since getSomeString returns an IO String, and the constructor expects a naked string, you can't just apply the constructor to the result of getSomeString

First you have to get that string actually produced, and then you can wrap it in the constructor:

do
    cs_one_str <- getSomeString
    let cs_one = MyCustomString cs_one_str
    ...

Or you can use the fmap function, which does the exact same thing as the snippet above:

    cs_one <- fmap MyCustomString getSomeString

Or its operator alias <$>:

    cs_one <- MyCustomString <$> getSomeString
Related