Lazy bytestring insanity - haskell

I'm really struggling to square this circle.
getPostContent uses Wreq to download a blog post and return it.
getPostContent url = do
let opts = defaults & W.checkStatus .~ (Just $ \_ _ _ -> Nothing)
postResp <- getWith opts $ baseUrl ++ url
if postResp ^. W.responseStatus . statusCode == 200
-- then return $ LEnc.encodeUtf8 $ postResp ^. W.responseBody . _String -- :: Prism T Text
then return $ postResp ^. W.responseBody . _String
else return "error downloading"
This is consumed by parseLBS
do
page <- getPostContent r -- :: IO String
let
-- parseLBS :: Data.ByteString.Lazy.Internal.ByteString -> Text.XML.Document
cursor = fromDocument $ parseLBS page
As I understand it, getPostContent is providing Data.Text.Text, whereas I need Data.ByteString.Lazy.Internal.ByteString and I cannot work out how to convert them ( thought it should be this, see code snippet above, but it does not compile either).
Couldn't match expected type ‘Data.ByteString.Lazy.Internal.ByteString’
with actual type ‘T.Text’
In the first argument of ‘parseLBS’, namely ‘page’
In the second argument of ‘($)’, namely ‘parseLBS page’
Compilation message with encode uncommented
Couldn't match type ‘TL.Text’
with ‘T.Text’
NB: ‘TL.Text’ is defined in ‘Data.Text.Internal.Lazy’
‘T.Text’ is defined in ‘Data.Text.Internal’
Expected type: (TL.Text -> Const TL.Text TL.Text)
-> Data.ByteString.Lazy.Internal.ByteString
-> Const TL.Text Data.ByteString.Lazy.Internal.ByteString
Actual type: (T.Text -> Const TL.Text T.Text)
-> Data.ByteString.Lazy.Internal.ByteString
-> Const TL.Text Data.ByteString.Lazy.Internal.ByteString
In the second argument of ‘(.)’, namely ‘_String’
In the second argument of ‘(^.)’, namely ‘responseBody . _String’

To summarise: encodeUtf8 is the right way to go. It seems the one you're using is from Data.Text.Lazy.Encoding, which requires a lazy Text. You can use Data.Text.Lazy.fromStrict to convert... Or you can look into Data.Text.Encoding, which works on strict Text (but then gives you a strict ByteString...)

Related

Allowing function to accept various types in Haskell

UPD I realized this question is not precisely correct for I was trying to do something which is common for me from Erlang experience. As answer explains my functions should be narrowed down in type, not type "extended" to adopt them...
(this is a test project while trying to learn/assess Haskell)
Here is a part of intended basic web-endpoint, returning either single entity (for request with id) or list of all (for request without id):
This naive code works:
doGet req resp =
let path = map T.unpack (pathInfo req)
in case path of
["molecule"] -> makeResponse resp doGetAllMolecules
["molecule", strId] -> makeResponse resp $ doGetMolecule strId
makeResponse resp f = do
res <- f
resp $ responseLBS status200 [] (LC8.pack $ (show res) ++ "\n")
doGetMolecule :: String -> IO (Maybe Db.Molecule)
doGetMolecule strId = Db.fetchMolecule (read strId :: Int)
doGetAllMolecules :: IO [Db.Molecule]
doGetAllMolecules = Db.fetchAllMolecules
Now I want it to return status404 when entity is not found:
makeResponse resp f = do
res <- f
let s = case res of
Nothing -> status404
_ -> status200
resp $ responseLBS s [] (LC8.pack $ (show res) ++ "\n")
this leads to error:
• Couldn't match type ‘[Db.Molecule]’ with ‘Maybe a0’
Expected type: IO (Maybe a0)
Actual type: IO [Db.Molecule]
• In the second argument of ‘makeResponse’, namely
‘doGetAllMolecules’
In the expression: makeResponse resp doGetAllMolecules
In a case alternative:
["molecule"] -> makeResponse resp doGetAllMolecules
So what is the way to "match on type" of the value, allowing it to be either Maybe or anything else?
I would do it this way:
doGetMolecule :: String -> IO (Maybe [Db.Molecule])
doGetMolecule strId = fmap (:[]) <$> Db.fetchMolecule (read strId :: Int)
doGetAllMolecules :: IO (Maybe [Db.Molecule])
doGetAllMolecules = Just <$> Db.fetchAllMolecules
Now they have the same type: both have a chance at returning a list of molecules, so doGet need only handle that one type.

In Yesod.Test, is it possible to get into `SpecM (TestApp site)` from `IO`, similarly to how `liftIO` allows the opposite?

More specifically, I'm looking for a function of type:
f :: site -> SpecM (TestApp site) b -> IO b
or similar.
In other words, I am looking for the inverse function of:
liftIO :: IO b -> SpecM (TestApp _site) b
I've looked in the Yesod.Test docs and source code and tried to construct it myself too, but I don't seem to find it anywhere and it doesn't seem to be simple to construct either. Have I overlooked something, and might it exist somewhere? Or could it be simple to construct in a way I have not considered?
Background
I've run into the need for such a function multiple times, but so far I've always been able to workaround or do without. But in my latest situation it doesn't seem possible to work around: I want to write a Monadic QuickCheck property that would set up an arbitrary db environment (runDB . insert) and run some handlers on it to test for certain invariants of my application.
This is the farthest I've gotten with my attempts:
propSpec = do
context "qt" $ do
it "monadic" $ property $ \a b -> monadicIO $ do
run $ withServer $ \site -> yesodSpec site $ runDB $ insert $ User{..}
assert $ a /= b
withServer cont = do
p <- runLogging $ withSqlitePool "database_test.db3" 10 $ \pool -> do
runSqlPool (runMigration migrateAll) pool
return pool
wipeDB (MyApp p)
cont (MyApp p, id)
Which, somewhat understandably, results in the following type error:
/path/to/project/spec/Spec.hs:301:35: error:
• Couldn't match type ‘hspec-core-2.2.4:Test.Hspec.Core.Spec.Monad.SpecM
() ()’
with ‘IO a0’
Expected type: IO a0
Actual type: Spec
• In the expression:
yesodSpec site $ runDB $ insert $ User{..}
In the second argument of ‘($)’, namely
‘\ site
-> yesodSpec site $ runDB $ insert $ User{..}’
In the second argument of ‘($)’, namely
‘withServer
$ \ site
-> yesodSpec site $ runDB $ insert $ User{..}’
/path/to/project/spec/Spec.hs:301:52: error:
• Couldn't match type ‘ST.StateT
(YesodExampleData MyApp) IO (Key User)’
with ‘transformers-0.5.2.0:Control.Monad.Trans.Writer.Lazy.WriterT
[YesodSpecTree (MyApp, a1 -> a1)]
Data.Functor.Identity.Identity
()’
Expected type: YesodSpec (MyApp, a1 -> a1)
Actual type: YesodExample MyApp (Key User)
• In the second argument of ‘($)’, namely
‘runDB $ insert $ User{..}’
In the expression:
yesodSpec site $ runDB $ insert $ User{..}
In the second argument of ‘($)’, namely
‘\ site
-> yesodSpec site $ runDB $ insert $ User{..}’
• Relevant bindings include
site :: (MyApp, a1 -> a1)
(bound at /path/to/project/spec/Spec.hs:301:27)
Any ideas?
Also, has anyone before used Test.QuickCheck.Monadic successfully in conjunction with Yesod.Test? Or if perhaps not successfully, at least attempted to do so?

Yesod: Using Github API v3 Library for Haskell

I'm working on a project that builds on the simple yesod template. I am new to functional programming, haskell and Yesod so it's probably something obvious to anyone with Yesod experience. At the moment I am trying to make github API calls using this library. I am getting some type issues and I'm not even sure how to start approaching solving them.
You can find my handler here.
Handler/Home.hs:43:19:
Couldn't match expected type ‘HandlerT
App IO (Either a0 GitHub.User)’
with actual type ‘GitHub.Request k0 GitHub.User’
In a stmt of a 'do' block:
possibleUser <- GitHub.userInfoForR "mike-burns"
In the expression:
do { maid <- maybeAuthId;
possibleUser <- GitHub.userInfoForR "mike-burns";
result <- either (("Error: " <>) . tshow) formatUser possibleUser;
defaultLayout
(do { (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<p>Your current auth ID: ");
(asWidgetT GHC.Base.. toWidget) (toHtml (show maid));
(asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"</p>\n");
.... }) }
Handler/Home.hs:44:38:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: a0 -> HandlerT App IO a1
Actual type: a0 -> Text
In the second argument of ‘(.)’, namely ‘tshow’
In the first argument of ‘either’, namely
‘(("Error: " <>) . tshow)’
Handler/Home.hs:44:45:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: GitHub.User -> HandlerT App IO a1
Actual type: GitHub.User -> Text
In the second argument of ‘either’, namely ‘formatUser’
In a stmt of a 'do' block:
result <- either (("Error: " <>) . tshow) formatUser possibleUser
The GitHub library seems to be about building requests, and running them. The userInfoForR does such a thing :
userInfoForR :: Name User -> Request k User
Once you have a request, you can run it with one of the following functions, depending if you need to authenticate or not:
executeRequest :: Auth -> Request k a -> IO (Either Error a)
executeRequest' :: Request RO a -> IO (Either Error a)
I don't know about this specific case, but let's say you don't need authentication. So, the following expression would do the trick:
executeRequest' (userInfoForR "mike-burns") :: IO (Either Error User)
Now, in order to use it in a Handler, you'll need to learn about the fact that Handler is an instance of MonadIO, and you can thus do:
euser <- liftIO (executeRequest' (userInfoForR "mike-burns"))
case euser of
Left rr -> ...
Right user -> ...

Blaze-html type error inside forM_ block

I'm just starting Haskell web development using Spock, persistent and blaze-html.
In one of the routes I have, I want to load every row in my selected tables. I do something like this:
get ("/show/flight/" <//> (var :: Var Integer)) $ \f -> requireUser $ \(_, l) -> do
fs <- runSQL $ loadFlightInfos f
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) loadFlightSeat
where
loadFlightSeat :: H.Html
loadFlightSeat =
forM_ fs $ \fs' -> do
sid <- runSQL $ getSeatIdByFlight fs' c
case sid of
Nothing -> H.div H.! A.class_ "alert alert-danger" $ "Oops, something went wrong! Please try again."
Just rid -> H.a H.! A.href (H.toValue $ "/flight/seat/" <> show c <> "/" <> show (fromIntegral $ (fromSqlKey . entityKey) sid)) H.! A.class_ "btn btn-theme" $ H.toHtml fs'
loadFlightInfos has type:
Integer -> SqlPersistM [Entity Flight]
and getSeatIdByFlight:
T.Text -> Integer -> SqlPersistM (Maybe (Entity Flight))
I copied runSQL from Spock's blog sample app, and it's something like this:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend) => SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
runSQL action = runQuery $ \conn -> runResourceT $ runNoLoggingT $ runSqlConn action conn
The type error I got:
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
I still don't understand this type error, because I know runSQL is a wrapper from persistent to Spock and if I simply just want to output HTML, why can't it pass type checking?
How do I resolve this type error?
Disclaimer: I didn't run your code.
I know runSQL is a wrapper from persistent to Spock
Exactly, and there lies your issue. The type of runSQL is:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend)
=> SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
The result type, (HasSpock m, SpockConn m ~ SqlBackend) => m a, tells us that runSQL gives a result in the Spock monad. Therefore, loadFlightSeat should be a Spock monad computation as well. However, you gave it type H.Html, which has nothing to do with the Spock monad. The issue will probably go away if you remove the mistaken type signature of loadFlightSeat and adjust your usage of loadFlightSeat accordingly:
flightSeat <- loadFlightSeat -- Returns an H.Html value in the Spock monad.
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) flightSeat
P.S.: The type error you got...
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
...is unusually weird because H.Html happens to be a synonym for MarkupM (), with MarkupM being a monad defined by blaze. As a consequence, the signature you gave to loadFlightSeat leads the compiler to attempt matching Spock's monad with MarkupM.

Snap render list in Hamlet

Given this little project I'm using to learn Haskell, I would like to move my request handler's code generation to a Hamlet template, but am unsure how to pass things around.
My current code generates the following error when lines are uncommented, which is the first blocker:
Couldn't match expected type `String -> String'
with actual type `String'
In the return type of a call of `renderHtml'
Probable cause: `renderHtml' is applied to too many arguments
In the expression: renderHtml ($ (shamletFile "fileList.hamlet"))
In an equation for `myTemplate':
myTemplate = renderHtml ($ (shamletFile "fileList.hamlet"))
Code:
site :: Snap ()
site =
ifTop (writeBS "hello world") <|>
route [ ("foo", writeBS "ba"),
("view_root_json_files", listRootFilesHandler)
] <|>
dir "static" (serveDirectory ".")
--myTemplate :: String -> String
--myTemplate = renderHtml ( $(shamletFile "fileList.hamlet") )
toText :: [FilePath] -> Text
toText = foldMap (flip snoc '\n' . pack)
listRootFilesHandler :: Snap ()
listRootFilesHandler = do
filenames <- liftIO $ getDirectoryContents "data"
let filtered_filenames = filter (not . isPrefixOf ".") filenames
writeText $ toText filtered_filenames
Ghc is telling you the correct type signature to put there. Just replace String -> String with String.

Resources