Monad Transformer and applicative Maybe - haskell

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.

Related

Purpose of re-boxing pure value for GHCI

Can someone explain to me why when using ghci I have to "re-box" the value returned by a monad?
listAction=listDirectory "D:\\"
lenAction=listAction>>=length
Error:
Couldn't match type `Int' with `IO b0'
Expected type: [FilePath] -> IO b0
Actual type: [FilePath] -> Int
I do not understand where my logic is flawed:
Using listAction gives me a IO [FilePath]
I unbox the value of listAction to [FilePath] and i give it to length
Now shouldn't length just print the result to the console being a pure function? Isn't it the same if i would say length [1,2,3]?
Why do i have to box it again? Isn't the result of the monad of type: [FilePath] and if so why can it not print the result?
lenAction=list>>=return . length
This just a type error.
The type of >>= (when used with IO) is
(>>=) :: IO a -> (a -> IO b) -> IO b
That is, the second argument must be a function that returns a value of type IO something. length doesn't have such a type, so the type checker complains.
As you show, return :: a -> IO a can be used to lift a value into IO. That is one way to solve the problem.
Another way is to use fmap:
fmap length listAction
This works because fmap (when used with IO) is
fmap :: (a -> b) -> IO a -> IO b
(Other ways of doing the same thing include liftM length listAction and length <$> listAction.)

Matching types and monads. Error

type C = StateT Int (ErrorT String IO)
main :: C ()
main = do
args <- liftIO $ getArgs
case args of
[] -> liftIO $ putStrLn "E"
(p:ps) -> s "d"
s :: String -> C ()
I've got the error:
Couldn't match type ‘Control.Monad.Trans.State.Lazy.StateT
Int
(Control.Monad.Trans.Error.ErrorT String IO)
()’
with ‘IO t0’
Expected type: IO t0
Actual type: C ()
In the expression: main
When checking the type of the IO action ‘main’
I cannot understand why there is an error and how to repair it. Please help.
main must be of type IO a for some a (usually, but not necessarily, ()). It can't be of type StateT Int (ErrorT String IO). You can fix this by using the functions provided with StateT and ErrorT for converting them to actions of the underlying monad.

Different type of function and `in do statement`

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.

In Yesod/Haskell, how do I use data from IO with the variable interpolation functionality?

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 I extract an IO () from Maybe and execute this?

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

Resources