Hello community thank you for your time.
I have an error and I am not sure what the error is, but what I think the problem is:
There is no IO transformer from ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
to Web.Scotty.Internal.Types.ScottyT
.
But I wondering why the compiler works with ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
. That's why I am working just with String and I removed all occurrences of {-# LANGUAGE OverloadedStrings #-}
but still get the error. On the other hand, this should be IO [String]
, shouldn't it?
And as you can mention I don't really know what ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
is.
At another place, I already use liftIO
successfully for an a -> IO String
function. And I think I use them the same way.
I think I get slowly a feeling for what a monad is, but not quite sure. I don't really know why I have to use a lift
function at all.
Error message:
• No instance for (MonadIO
(Web.Scotty.Internal.Types.ScottyT
text-1.2.4.1:Data.Text.Internal.Lazy.Text IO))
arising from a use of ‘liftIO’
• In a stmt of a 'do' block:
paths <- liftIO $ getAllFilePaths2 path
In the expression:
do paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
In an equation for ‘pathsToScotty2’:
pathsToScotty2 path
= do paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
|
49 | paths <- liftIO $ getAllFilePaths2 path
Where the error occurred:
import Control.Monad.IO.Class
...
pathsToScotty2 :: String -> ScottyM ()
pathsToScotty2 path = do
paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
getAllFilePaths2 :: String -> IO [String]
getAllFilePaths2 dir = do
putStrLn dir
isFile <- doesFileExist dir
if isFile
then return [dir]
else do
dirs <- listDirectory dir
foldl foldHelper2 (return []) $ map (\d -> show $ mconcat [dir, "/",d ]) dirs
foldHelper2 :: IO [String] -> String -> IO [String]
foldHelper2 ps path = do
paths <- ps
newPaths <- getAllFilePaths2 path
return (paths ++ newPaths)
Truly understanding monads takes time, practice, and patience, but it shouldn't be too hard to understand the need for liftIO
by examining your types.
First off, the type of liftIO
is MonadIO m => IO a -> m a
. This means that the function can convert any IO action into an action in the monad m
so long as m
has an instance of MonadIO
. In theory, this can only be implemented if m
has some way of processing IO
actions, so this function is embedding the given action into the m
monad.
You're definitely in the right sort of place to use liftIO
, so why isn't it working? That is, you have a value getAllFilePaths2 path
of type IO [String]
, and you'd like it to be a value of type ScottyM [String]
— this indeed seems like a good place to use liftIO
. However, ScottyM
is not an instance of MonadIO
, as that error message you saw is trying to tell you, so you can't use liftIO
.
This may seem crazy—can you really not embed IO actions into ScottyM
?—but there's actually a good reason for this. What happens if the IO
action throws an error? Does your whole web app crash? It would if you naively used liftIO
. Instead, scotty provides the function liftAndCatchIO
, which, as the docs describe, is "Like liftIO
, but catch any IO exceptions and turn them into Scotty exceptions." This is the preferred way to embed IO
actions into Scotty.
And here comes the final gotcha: Note that liftAndCatchIO
actually produces values of type ActionM a
, not ScottyM a
. Additionally, there's no way to take a value in the ActionM
monad and get it into the ScottyM
monad. Instead, you need to use that value as an action. So, I'm not sure what pathsToScotty
does, but it's very likely that you'll need to rewrite it.