Question

Today I have tried to concat two IO Strings and couldn't get it work.

So, the problem is: suppose we have s1 :: IO String and s2 :: IO String. How to implement function (+++) :: IO String -> IO String -> IO String, which works exactly as (++) :: [a] -> [a] -> [a] but for IO String?

And more general question is how to implement more general function (+++) :: IO a -> IO a -> IO a? Or maybe even more general?

Était-ce utile?

La solution

You can use liftM2 from Control.Monad:

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c


> :t liftM2 (++)
liftM2 (++) :: Monad m => m [a] -> m [a] -> m [a]

Alternatively, you could use do notation:

(+++) :: Monad m => m [a] -> m [a] -> m [a]
ms1 +++ ms2 = do
    s1 <- ms1
    s2 <- ms2
    return $ s1 ++ s2

Both of these are equivalent. In fact, the definition for liftM2 is implemented as

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f m1 m2 = do
    val1 <- m1
    val2 <- m2
    return $ f val1 val2

Very simple! All it does is extract the values from two monadic actions and apply a function of 2 arguments to them. This goes with the function liftM which performs this operation for a function of only one argument. Alternatively, as pointed out by others, you can use IO's Applicative instance in Control.Applicative and use the similar liftA2 function.

You might notice that generic Applicatives have similar behavior to generic Monads in certain contexts, and the reason for this is because they're mathematically very similar. In fact, for every Monad, you can make an Applicative out of it. Consequently, you can also make a Functor out of every Applicative. There are a lot of people excited about the Functor-Applicative-Monad proposal that's been around for a while, and is finally going to be implemented in an upcoming version of GHC. They make a very natural hierarchy of Functor > Applicative > Monad.

Autres conseils

import Control.Applicative (liftA2)

(+++) :: Applicative f => f [a] -> f [a] -> f [a]
(+++) = liftA2 (++)

Now in GHCI

>> getLine +++ getLine
Hello <ENTER>
World!<ENTER>
Hello World!
(++) <$> pure "stringOne" <*> pure "stringTwo" 

implement function (+++) ... which works exactly as (++) :: [a] -> [a] -> [a] but for IO String?

Don't do that, it's a bad idea. Concatenating strings is a purely functional operation, there's no reason to have it in the IO monad. Except at the place where you need the result – which would be somewhere in the middle of some other IO I suppose. Well, then just use do-notation to bind the read strings to variable names, and use ordinary (++) on them!

do
  print "Now start obtaining strings..."
  somePreliminaryActions
  someMoreIOStuff
  s1 <- getS1
  s2 <- getS2
  yetMoreIO
  useConcat'dStrings (s1 ++ s2)
  print "Done."

It's ok to make that more compact by writing s12 <- liftA2 (++) getS1 getS2. But I'd do that right in place, not define it seperately.

For longer operations you may of course want to define a seperate named action, but it should be a somehow meaningful one.

You shouldn't think of IO String objects as "IO-strings". They aren't, just as [Int] aren't "list-integers". An object of type IO String is an action which, when incurred, can supply a String object in the IO monad. It is not a string itself.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top