this is probably a simple question and I've seen a similar one on SO, but I'm still stuck.
I'm trying to do an HTTP call to pull in the contents of another blog and display it on my page. This is more of a learning exercise than anything.
Here's my handler
blog :: App1Handler ()
blog = do
contents <- Requester.getUrl "http://someblog.com/"
heistLocal (bindString "contents" contents) . render $ "blog"
Requester.getUrl has the signature getUrl :: String -> IO T.Text
And the error I get back is
src/Main.hs:50:15:
Couldn't match expected type Handler App1 App1 t0' with actual typeIO T.Text'
In the return type of a call of `getUrl'
In a stmt of a 'do' block:
contents <- getUrl "http://someblog.com/"
In the expression:
do { contents <- getUrl "http://someblog.com/";
heistLocal (bindString "contents" contents) . render $ "blog" }
From what I gather, I'm stuck inside of the IO monad and it wants the type Handler App1 App1 t0. I've experimented with sticking liftIO in places, but I'm pretty confused on this one.
Can anyone point me in the right direction?
Thanks!
You just have to liftIO the IO action returned by getUrl, like this:
contents <- liftIO $ Requester.getUrl "http://someblog.com/"
The reasoning here is simple. You have a do-block of type App1Handler (), which means that the
right hand side of any <- statement within this do-block must have type App1Handler a.
However, getUrl returns IO Text, so you need to a function to convert from
IO a to App1Handler a which is exactly what liftIO does.
liftIO :: MonadIO m => IO a -> m a
Related
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.
Visualize a bytestring body on a webserver run on Spock (localhost for instance)
My goal : create website and view a bytestring (converted to text)
Framework: Http Simple for performing request to restAPI
Spock for my server
I don't want for instance to create a JSON as I need to manipulate/inspect my response before creating a JSON structure. General idea is that I want to use the response body to construct a JSON query structure (the user will be able to compose his question) that will be sent to the restAPI website.
I manage to build a request like this:
connect = do
request' <- (parseRequest "http://localhost")
let request = setRequestMethod "POST"
$ setRequestHost (S8.pack ("xx.xxx.xxx.xxx"))
$ setRequestPath "/api/Integration/Login"
$ setRequestBodyJSON me
$ setRequestPort 1000
$ request'
response <- httpJSON request
return (getResponseBody response :: Auth)
then I used it to query the API page
getRequest :: RequestPath -> HtmlT IO L.ByteString
getRequest rpath = do
atoken <- liftIO connect
request' <- liftIO (parseRequest "http://localhost")
let request = setRequestMethod "POST"
$ setRequestHost (S8.pack ("xx.xxx.xxx.xxx"))
$ setRequestPort 1000
$ setRequestPath (S8.pack ("/api/Integration/" ++ rpath))
$ addRequestHeader hAuthorization (S8.pack (unpack (token_type (atoken)) ++ " " ++ unpack (access_token (atoken))))
$ setRequestBodyJSON r1
$ request'
response <- httpLBS request
return (getResponseBody (response))
then I follow with a short SpockM monad:
app1 = do get root $ text "root"
fct
with fct equal to
fct = do get "/further" $ lucidIO ( fmap TL.decodeUtf8 (getRequest "GetProperties"))
Everything compile fine I am even able to see the result in GHCI with invocation like : connect >>= (\ x -> print x) (same with getRequest "GetProperties" )
What I don't understand is that lucidIO should give me a ActionCtxtT ctx m b type, which perfectly fit the type of a handler (for example like the text function in the do get ... $ text -> ActionCtxT ctx m a) and should be processed by the spock function in main() ie runSpock 8080 (spock spockCfg app1)
I tried to get rid of the ByteString 'ending' type replacing it with a () in order to mimic as close as possible the Html () type which shows up and work in lot of examples I studied.
All parsing and request building is done with the HTTP.Simple (it's not very elegant I know for instance it just have to work) which pulls me from start in a monad (due to the first function 'parseRequest' -> m Request) from which I cannot escape until lucidIO - may be I am choosing the wrong Monad (ie IO : but with IO I am able to check everything in ghci). Could you give me some hints on how to get this ByteString printed in my browser?
So finally I achieve what I was looking for - woua I am really proud of me ...
Okay for those who will look for the same thing, what I've manage to do, to recap my main problem was to escape the IO monad (my choice may be not clever but still) in which I was stuck due to the use of request parsers from HTTP.simple library.
My code change a little bit but the general idea stays the same:
building a Response query:
getResponseMethod :: RequestPath -> RequestBody -> IO (Maybe Value)
from which thanks to the decode function (aeson package) a Maybe Value is obtained (wrapped in IO but that's okay)
then my little spock server:
main :: IO ()
main = do
spockCfg <- defaultSpockCfg () PCNoDatabase ()
runSpock 8080 (spock spockCfg app)
I work a lot to have the right app -> SpockM () () () ()
I started with the simplest app we could imagine:
app = do get root $ text "Hello!"
noticing that the text function is producing a MonadIO m => ActionCtxT cxt m a monad so my thought was that if I 'sprinkle' some clever LiftIO thing it should do the job.
I create a helper function:
extrct :: MonadIO m => ActionCtxT ctx m Text
extrct = liftIO $ do
a <- getResponseMethod "GetProperties" r1
return (pack $ show a)
and with a twist of hand adjust my app
app :: SpockM () () () ()
app = do get root $ do
a <- extrct
text a
and finally I was able to see the string representation of the Maybe Value :: JSON on my spock local webserver. That's what I was looking for. Now I can work on cleaning my code. From what I understand using liftIO will place the IO monad in the rigth place in the Monad Stack that's because IO is always at the bottom?
In Haskell, the type constructor IO is a monad equipped with a return statement that lifts any expression to its IO version.
Nothing prevents us from lifting what is already an IO action to its IO version - giving us a type of the form IO (IO a).
So I can for example write the following program:
main = return . print $ "Hello world"
Which upon execution does exactly nothing.
My question is, what happens under the hood when this main is executed?
Are there any situations where it makes sense to return an IO action?
Under the hood, the runtime effectively discards the result of the IO action main, which is why it is usually defined as IO (). This means that if main actually has a type like IO (IO Int), there is no real issue. The IO action is executed, and the result (another IO action) is discarded, unexecuted.
Deeper in your program, you are more likely to just trigger a type error. For example, fmap doSomething (return . getLine) won't type-check if you meant fmap doSomething getLine.
IO is often approximated to State RealWorld, that is a State monad that operates on the "real world". return for State produces an action that does not alter contained state. So, by analogy, returning anything to IO doesn't do anything as well. Not only when you return some IO a, but any a.
Returning an IO action may be used to build up a computation in one place (capturing some values into a closure along the way) and execute it somewhere else. Much like passing a callback in C.
Actually, to build up an IO computation and pass it somewhere else you can use a let inside of a do-block:
main :: IO ()
main = do
let act = readFile "somefile" -- action is not performed yet
foo act -- pass the action as a parameter
foo :: IO () -> IO ()
foo act = do
.. do something
result <- act -- actually perform the action
..
In this case you don't need to return an IO a value.
But if the process of building that computation itself requires you to perform IO actions, that's where you'd need such a thing. In our example, let's ask user of filename to be opened:
main :: IO ()
main = do
act <- do
filename <- getLine
return (readFile filename)
foo act
Here, we need a filename to create our action, but getLine is in IO as well. That's where additional level of IO comes out. Of course, this example is synthetic, as you can just do filename <- getLine right in main.
I am toying around with the Snap framework and I often encounter
the case where I do a database lookup based on a parameter I get
from a form field.
Consider e.g. the following two functions
getParam :: (MonadSnap m) => ByteString -> m (Maybe ByteString)
doLookup :: (MonadIO (m b v), MonadSnaplet m, MonadState s (m b b), HasAcid s UrlDB) => ByteString -> m b v (EventResult QueryByURL)
where UrlDB is a mapping between Integers and URLs. The complicated type signature of the second
function is due to the use of acid-state and eventually results in Maybe Integer.
queryByURL :: Text -> Query UrlDB (Maybe Integer)
So far, my handler looks like
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
mUrl <- getParam "url"
case mUrl of
Nothing -> render "index"
Just url -> do
mId <- doLookup $ url
case mId of
Nothing -> render "index"
Just i -> do
fancyStuffWith i
render "index"
Now, the first thing that annoys me is the staircasing of the case expressions.
The second thing is the threefold appearance of render "index".
Basically, whenever one of the two Maybe values is Nothing, I want to return
a default view.
What would be the cleanest way to do this?
This is what the MaybeT monad transformer is for. Your code could be written like this.
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
runMaybeT $ do
url <- MaybeT $ getParam "url"
i <- MaybeT $ doLookup url
fancyStuffWith i
render "index"
The errors package pulls together these things and adds a lot of convenience functions for working with them. In addition to MaybeT, the EitherT monad transformer does something similar but keeps track of error messages so you can track when your computation failed.
To avoid repeating render "index", you have to see that you basically call it at the end of all code pathes. Then you can try to abstract that pattern matching part with a function. The result is not perfect but slightly better.
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
withJust $ getParam "url" $ \url ->
withJust $ doLookup url $ fancyStuffWith
render "index"
where
withJust :: (IO (Maybe a)) -> (a -> IO()) -> IO ()
withJust iomval cmd = do
mval <- iomval
case mval of
Nothing -> return ()
Just val -> cmd val
the withJust function performs an IO action which might fail to bring a value. If it succeeds, the value is passed to another command.
Following the example file, I try to dynamically call an IO action.
testHint :: Interpreter ()
testHint = do
setImportsQ [("Prelude", Nothing)]
let somecode = "putStrLn \"some string\""
interpret somecode (as :: IO ())
say "hello"
Unfortunately, the string "some string" is never printed. I also tried to call writeFile but no file was created, either.
So what is needed to enable these side effects? Thank you!
interpret won't run any IO actions on its own. It'll just evaluate it and return it to you and then you have to run it yourself, for example using liftIO:
interpret somecode (as :: IO ()) >>= liftIO