I have this code:
trick = Just (putStrLn "Hello?")
And I want to unwrap this IO () out the Maybe context and call it.
main = do foo <- trick
foo
However, this throws an error:
Couldn't match type ‘IO’ with ‘Maybe’
Expected type: Maybe ()
Actual type: IO ()
How could I resolve this?
The function you are looking for is Data.Foldable.sequence_:
>>> Data.Foldable.sequence_ (Just (putStrLn "Hello?"))
Hello?
>>>
If your Maybe is Nothing, it will not do anything:
>>> Data.Foldable.sequence_ Nothing
>>>
This works because the type of Data.Foldable.sequence_ is:
Data.Foldable.sequence_
:: (Foldable t, Monad m) => t (m a) -> m ()
... and if you specialize t to Maybe and m to IO, you get:
Data.Foldable.sequence_ :: Maybe (IO a) -> IO ()
In the specific context of Maybe, it's equivalent to:
sequence_ (Just io) = do
_ <- io
return ()
sequence Nothing = return ()
The easiest solution to start out with is probably to realize that you can decide what to do based on the value of the Maybe (IO ()) via pattern matching.
maybeDoIO :: Maybe (IO ()) -> IO ()
maybeDoIO (Just io) = io
maybeDoIO Nothing = return ()
You have to take the Nothing into account, basically:
main = maybe (putStrLn "Nothing") id foo
The problem with your main function is that you are mixing two different monads in the same do block.
The foo <- trick action is "relative" to the Maybe monad but the foo action afterwards is an IO action. The type for >>= is:
Monad m => m a -> (a -> m b) -> m b
But in your case you'd need something with type:
Maybe a -> (a -> IO b) -> IO b
with two different monads.
If you want to execute the IO action then the type of main must be of the kind IO a so the do notation must refer to IO and not Maybe. Which means that you cannot use <- to extract the action but you have to use something else. For example Data.Maybe.fromMaybe:
import Data.Maybe
main = do fromMaybe (return ()) trick
Related
I have a function that requires the handling of two Maybes. This is an extremely naive implementation:
go :: IO (Maybe Int)
go = do
res1 <- firstFunction
case res1 of
Nothing -> return Nothing
Just a -> do
res2 <- secondFunction a
case res2 of
Nothing -> return Nothing
Just b -> return b
How could I structure this so that I can use the result a of the firstFunction in my secondFunction?
Use MaybeT IO a instead of IO (Maybe a). Then:
firstFunction :: MaybeT IO X
firstFunction = undefined -- ...
secondFunction :: X -> MaybeT IO Int
secondFunction = undefined -- ...
go :: IO (Maybe Int)
go = do
a <- firstFunction
secondFunction a
-- OR
go = firstFunction >>= secondFunction
You may use these two halves of an isomorphism to convert between the two types:
MaybeT :: IO (Maybe a) -> MaybeT IO a
runMaybeT :: MaybeT IO a -> IO (Maybe a)
To reduce the total number of uses of them, I recommend pushing the former as deep into the leaves of your computation as you can, and lifting the latter as far out as you can. You may also like these actions for defining leaf computations:
lift :: IO a -> MaybeT IO a
liftIO :: IO a -> MaybeT IO a
empty :: MaybeT IO a -- for when you want a Nothing
pure :: a -> MaybeT IO a -- for when you want a Just
maybe empty pure :: Maybe a -> MaybeT IO a
Inside a do block of a ExceptT String IO ()
I have a function that produces a ReaderT like so:
type UDCEnv = (AWS.Env, Bool)
uploadVersionFilesToCaches :: S3.BucketName
-> FilePath
-> [GitRepoNameAndVersion]
-> ReaderT UDCEnv IO ()
I just so happen to have a Maybe FilePath so I create my ReaderT like so:
let maybeReader :: Maybe (ReaderT UDCEnv IO ()) =
uploadVersionFilesToCaches s3BucketName <$> maybeFilePath <*> Just gitRepoNamesAndVersions
I can even run the ReaderT like so:
let maybeIO :: Maybe (IO ()) =
runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose)
Everything works fine as long as I use let expressions. As soon as I drop the let in the expression above to actually try to have expression evaluated Applicative gets types as ExceptT String IO FilePath instead of Maybe
The parts I am omitting are marked by ... :
f :: ... -> ExceptT String IO ()
f ... = do
...
runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose) -- Error here
undefined
Produces
Couldn't match type ‘IO ()’ with ‘()’
Expected type: ReaderT UDCEnv IO () -> UDCEnv -> ()
Actual type: ReaderT UDCEnv IO () -> UDCEnv -> IO ()
In the first argument of ‘(<$>)’, namely ‘runReaderT’
In the first argument of ‘(<*>)’, namely
‘runReaderT
<$>
(uploadVersionFilesToCaches s3BucketName <$> maybeFilePath
<*> Just gitRepoNamesAndVersions)’
/Users/blender/Code/Personal/Haskell/Rome-Public/src/Lib.hs: 82, 73
Couldn't match type ‘Maybe’ with ‘ExceptT String IO’
Expected type: ExceptT String IO FilePath
Actual type: Maybe FilePath
In the second argument of ‘(<$>)’, namely ‘maybeFilePath’
In the first argument of ‘(<*>)’, namely
‘uploadVersionFilesToCaches s3BucketName <$> maybeFilePath’
I think the first error is because I'm missing some liftIO somewhere.
However I have no idea what to do about the misunderstood Applicative.
I could case analysis on the Maybe of course instead of using Applicative but I would really prefer not to.
Edit: Oops, fixed a bug.
There seems to be a minor inconsistency in your question, because the do-block you provide contains a runReaderT ... expression that doesn't match the expression given in your error message.
However, ultimately the problem is this: in a do-block of type m a for some monad m, each plain expression (and each right-hand side of an x <- y expression) has to have type m b for some b. So, by using your runReaderT ... expression in a do-block of type ExceptT String IO (), you're forcing Haskell to type-check it as ExceptT String IO a for some a. However, it's a Maybe (IO ()), so that type-checking will fail.
You'd get a similar error if you tried:
foo :: ExceptT String IO ()
foo = do Just (putStrLn "won't work") -- has type Maybe (IO ())
undefined
You need to decide how to adapt the runReaderT ... expression to the surrounding do-block. Two reasonable options are:
foo = do ...
maybe (throwError "reader was Nothing!") liftIO
$ runReaderT ...
undefined
which will throw an ExceptT-style error if your maybeReader is Nothing or:
foo = do ...
maybe (return ()) liftIO
$ runReaderT ...
undefined
which will do .. erm .. nothing in case of Nothing.
main :: IO ()
main = do
args <- getArgs
case args of
[] -> putStrLn "Give an argument!"
It works, but why? Returned type is IO (). getArgs returned IO [String] ( not IO ()). So it should raise an error.
type C = ErrorT String IO
main :: C ()
main = do
args <- getArgs
case args of
[] -> putStrLn "Give an argument!"
putStrLn tries to return IO (). But it isn't compatible with C. So, should I liftIO? I see that I should. Could you show me how does liftIO works here? Why I have to use liftIO with getArgs?
To figure out what is going on, you can try de-sugaring you function:
main :: IO ()
main = getArgs >>= \ args ->
case args of
[] -> putStrLn "Give an argument!"
Here, getArgs has type IO [String] and the function (let's call it f) has type [String] -> IO (). Now lets see what's going on with (>>=):
(>>=) :: m a -> (a -> m b) -> m b
-- a unifies with [String] and m with IO
(getArgs >>=) :: ([String] -> IO b) -> IO b
(>>=) :: IO [String] -> ([String] -> IO b) -> IO b
-- b unifies with ()
getArgs >>= f :: IO ()
(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()
For your example with C, there are three things wrong:
GHC expects main to have type IO (), but it has type C () in your snippet
getArgs is expected to have type C [String], but it has type IO [String]
same for putStrLn, which must have type C ()
To fix this, since ErrorT is a monad transformer, you can lift a monadic action to the level of the transformer with lift.
liftIO is a lift operation that can be applied only with monad transforming IO. The interesting thing is that it can work with any monad transformer (with as much monad transformer depth as you want): With liftIO, you can work with T1 (T2 (... (Tn IO)...)) a for any n where Tn are monad transformers.
You can notice their kind of similar signatures:
lift :: (MonadTrans t, Monad m) => m a -> t m a
liftIO :: MonadIO m => IO a -> m a
You don't need to specify IO for m, in the signature of liftIO, because it's instance of MonadIO already tells us that it can perform only IO actions.
To conclude, this is how you would use liftIO in your program:
type C = ErrorT String IO
main :: IO ()
main = runErrorT go >>= print -- print the result of type `Either String ()`
where go :: C ()
go = do
args <- liftIO getArgs
case args of
[] -> liftIO $ putStrLn "Give an argument!"
But, here, since you don't use any features of the ErrorT monad, the code is not really a good example.
Also, note that if you are talking about ErrorT in the mtl package, it is deprecated, consider using ExceptT.
Check out this post for more information about lift vs liftIO
If you desugar the do notation:
main = getArgs >>= (\ args -> case args of [] -> putStrLn "Argh!")
getArgs has type IO [String]. >>= pipes the resulting [String] into the lambda, which returns IO (), the result of putStrLn "...". Here, >>= has the inferred type:
(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()
So the type of the whole expression is IO (), matching the signature of main.
How do I take values from an IO monad and interpolate into a yesod widget?
For example, I want to interpolate the contents of a file into hamlet:
(readFile "test.txt") >>= \x -> toWidget $ [hamlet| <p> text: #{x} |]
or equivalently:
contents <- (readFile "test.txt")
toWidget $ [hamlet| <h2> foo #{contents} |]
There's something basic I'm not grasping about how interpolation interacts with IO since neither of these type checks:
Couldn't match type ‘IO’ with ‘WidgetT App IO’ …
Expected type: WidgetT App IO String
Actual type: IO String
These errors occur in a getHomeR route function.
If I try to do something similar in GHCi with a predefined function, I get a different error. In the source file, I have a function:
makeContent body =
[hamlet|
<h2> foo
<div> #{body}
|]
in GHCi:
(readFile "test.txt") >>= \x -> makeContent x
And I get an error due to insufficient arguments (I think this is because of some template magic I don't understand yet):
<interactive>:139:33:
Couldn't match expected type ‘IO b’
with actual type ‘t0 -> Text.Blaze.Internal.MarkupM ()’
Relevant bindings include it :: IO b (bound at <interactive>:139:1)
Probable cause: ‘makeContent’ is applied to too few arguments
When working with monad transformers, to convert from some monad m to a transformer t m, you have to use the lift function:
lift :: (MonadTrans t, Monad m) => m a -> t m a
This is actually the defining method of the MonadTrans typeclass, so the implementation is specific to the transformer t.
If you want to perform IO actions inside a transformer, you'll have to define an instance for MonadIO, which has the liftIO method:
liftIO :: (MonadIO m) => IO a -> m a
an instance of MonadIO doesn't have to be a transformer, though, IO is an instance where liftIO = id. These two functions are designed to let you "pull" actions up a transformer stack, for each level in the stack you would call lift or liftIO once.
For your case, you have the stack WidgetT App IO, with the transformer WidgetT App and the base monad IO, so you only need one call to liftIO to pull an IO action up to be an action in the WidgetT App IO monad. So you would just do
liftIO (readFile "test.txt") >>= \x -> makeContent x
A lot of developers (including myself) find liftIO a bit of a burden to type when you have a lot of IO actions, so it's not uncommon to see something like
io :: MonadIO io => IO a -> io a
io = liftIO
putStrLnIO :: MonadIO io => String -> io ()
putStrLnIO = io . putStrLn
printIO :: (MonadIO io, Show a) => a -> io ()
printIO = io . print
readFileIO :: MonadIO io => FilePath -> io String
readFileIO = io . readFile
And so on. If you find yourself using a lot of liftIOs in your code, this can help trim down on how many characters you need to type.
How can a output a list of strings using putStr and map? I want to do something like:
s=["test1","test2"]
map putStr s
But have no experience with monads and don't know how to get it right...
Any kind of hint is enormously welcome!
The correct thing to do here is use mapM_ :: Monad m => (a -> m b) -> [a] -> m (). But if you'd like to learn a little about some useful Prelude functions...
We have two functions here,
map :: (a -> b) -> [a] -> [b]
putStr :: String -> IO ()
if we substitute the type of putStr into map (ie. a ~ String, b ~ IO ()) we get
map putStr :: [String] -> [IO ()]
so this takes a list of strings and gives us a list of IO () actions; IO computations that don't return anything usefull.
We'd like to turn that [IO ()] into something like IO (...) because we need IO to be the outermost layer in order to use it in main :: IO ()
The function sequence :: Monad m => [m a] -> m [a] from the Prelude is exactly what we need. It takes a list of monadic actions, executes them and returns the results in a list wrapped in the monad. Intuitively it moves the monad to the outermost layer.
sequence . map putStr :: [String] -> IO [()]
This is quite close, but we still have IO [()] instead of IO (), we don't really care about the [()] result and it'd be nice to ignore it. Again, the Prelude has what we need: sequence_ :: Monad m => [m a] -> m () which executes each monadic action in the list and ignores their return values.
Note that mapM_ is defined as
mapM_ f = sequence_ . map f
The type of map is:
map :: (a -> b) -> [a] -> [b]
this means that, since putStr returns an IO (), your expression will return a [IO ()].
You can, instead, use mapM_ (imported from Prelude) that will ignore the return type from the mapping and return an IO () which is a suitable return type for main:
main = do
let s=["test1","test2"]
mapM_ putStr s
Live demo