Haskell Snap: query_ trouble - haskell

I got trouble with ambiguous type when developing web app with Snap.
My code is
getUserByUsrnamePwd :: Handler HaskellCalendar HaskellCalendar ()
getUserByUsrnamePwd = do
username <- getPostParam "username"
password <- getPostParam "password"
user <- query_ "SELECT * FROM users WHERE username = ? AND password = ?" (username, password)
liftIO $ print (user :: [User])
I got an error
Expected type: (Maybe ByteString, Maybe ByteString)
-> Handler HaskellCalendar HaskellCalendar [User]
Actual type: (Maybe ByteString, Maybe ByteString) -> [r0]
I really don't know why because according to API it should be (Maybe ByteString, Maybe ByteString) rather than (Maybe ByteString, Maybe ByteString) -> [r0]
Kind Regards

Judging by the documentation, it looks like query_ is only for queries that take no parameters. You want query.

Related

Do block resolve types

I just start coding in Haskell recently and I start getting use of do block. I'm coming from Scala world, and after read a book and some blogs I see that do block was the inspiration of our from comprehension. But still I´m struggling with the arguments that pass in every function as input -> output
Here in my code I'm using scotty http server library to get a request and I'm trying to persist that request in mySQL.
But in this line where I try top get the value from the Maybe to send to the another function to be persisted
user <- (fmap (\user -> insertUser user) maybeUser)
It never compile since the function does not return the expected type
ActionT Text IO (Maybe User)
but
IO User
Here my hole program
createUser :: ActionM ()
createUser = do maybeUser <- getUserParam
-- Persist the user
_ <- persistUser
json maybeUser
getUserParam :: ActionT Text IO (Maybe User)
getUserParam = do requestBody <- body
return (decode requestBody)
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
persistUser _maybeUser = let maybeUser = _maybeUser in do
user <- maybeUser
user <- (fmap (\user -> insertUser user) maybeUser)
return maybeUser
insertUser :: User -> IO User
insertUser _user = let user = _user in do
conn <- createConnection
status <- execute conn insertUserQuery [MySQLInt32 (intToInt32 $ getUserId user), MySQLText "hello_haskell_world"]
return user
Let's consider the following function:
persistUser :: Maybe User -> ActionT Text IO (Maybe User)
A value of type Maybe User is passed as an argument, and we need this user to be inserted into database. In order to do that, we can use (<$>) (or fmap) function as:
insertUser <$> maybeUser
The resulting type is: Maybe (IO User). Now we need to lift this type to ActionT Text IO (Maybe User) somehow.
Web.Scotty.Trans has a liftAndCatchIO function (also available in Web.Scotty module), which mostly does what we need, but it accepts IO a as an argument, so we need to "swap" Maybe and IO. Let's find a function for this. So sequence does what we need.
As a result, we have the following implementation of persistUser function:
persistUser maybeUser =
liftAndCatchIO $ sequence $ insertUser <$> maybeUser

Extract value from Maybe in do block

Hi I have a piece of code where I retrieving a Maybe User where User is my own type.
getUserById :: Int -> IO (Maybe User)
getUserById id = let userId = id in do
conn <- createConnection
(columnDef, inputStream) <- query conn selectByIdQuery [One $ MySQLInt32 (intToInt32 userId)]
maybeMySQLValue <- Streams.read inputStream
return (transformToUser <$> maybeMySQLValue)
But the key is that the function that call this functions expect a IO User and not a IO Maybe User
getUserById :: Int -> IO User
Any advice about how to extract in the do block the value from the Maybe?
I´m trying this but still does not work
user <- extractMaybeUser (transformToUser <$> maybeMySQLValue)
return user
extractMaybeUser :: Maybe User -> User
extractMaybeUser maybeUser = case maybeUser of
Just value -> value
Nothing -> User 1 "default User"
Regards
user <- extractMaybeUser (transformToUser <$> maybeMySQLValue)
The reason why this code might not work is that extractMaybeUser has return type of User:
extractMaybeUser :: Maybe User -> User
There is no monadic value to extract from using <-, so what about just wrapping in IO the user we get from extractMaybeUser?
return (extractMaybeUser (transformToUser <$> maybeMySQLValue))
It will at least have IO User type

How should I create a data structure from multiple network requests in Haskell

I'm new to Haskell so apologies in advance for the potentially stupid question.
I'd like to build a data structure that is constructed from two http requests in my application.
My first request gets a basic list of users which I could choose to decode to Maybe [User]
r <- getWith opts "https://www.example.com/users"
let users = decode $ r ^. responseBody :: Maybe [User]
But if I'd like to enrich my user data by calling a second endpoint for each of the users that respond by doing something like
r2 <- getWth opts "https://www.example.com/users/{userid}/addresses"
let enrichedUser = decode $ r2 ^. responseBody :: Maybe EnrichedUser
I can't quite piece these parts together at the minute. I'm in a do block thats expecting an IO ()
Any help would be appreciated!
I'm assuming that the type of enrichedUser is supposed to be Maybe EnrichedUser and not Maybe [EnrichedUser], right?
If so, after extracting the [User] list from users :: Maybe [User], the problem you're facing is running a monadic action (to fetch the web page) for each User. There's a handy combinator for this in Control.Monad:
mapM :: (Monad m) => (a -> m b) -> ([a] -> m [b])
which can be specialized in your situation to:
mapM :: (User -> IO EnrichedUser) -> ([User] -> IO [EnrichedUser])
This says, if you know how to write a function that takes a User and creates an IO action that will create an EnrichedUser, you can use mapM to turn this into a function that takes a list [User] and creates an IO action to create a whole list [EnrichedUser].
In your application, I imagine the former function would look something like:
enrich :: User -> IO EnrichedUser
enrich u = do
let opts = ...
let url = "https://www.example.com/users/"
++ userToUserID u ++ "/addresses"
r2 <- getWith opts url
let Just enrichedUser = decode $ r2 ^. responseBody
return enrichedUser
where decode = ...
and then you can write (in your IO do-block):
r <- getWith opts "https://www.example.com/users"
let Just users = decode $ r ^. responseBody
enrichedUsers <- mapM enrich users
-- here, enrichedUsers :: [EnrichedUser]
...etc...
I've omitted the Maybe processing here for simplicity. If enriching fails, you probably want to somehow coerce a regular User into a default EnrichedUser anyway, so you'd modify the bottom of the enrich function to read:
let enrichedUser = case decode $ r2 ^. responseBody of
Nothing -> defaultEnrichment u
Just e -> e
return enrichedUser
and everything else would stay the same.

Haskell bind with multiple monads

Right now my code looks like this:
postUser :: ServerPart Response
postUser = do
-- parseBody :: ServerPart (Maybe User)
parsedUser <- parseBody
case parsedUser of
Just user -> do
-- saveUser :: User -> ServerPart (Maybe (Key User))
savedUser <- saveUser user
case savedUser of
Just user -> successResponse
Nothing -> errorResponse "user already exists"
Nothing -> errorResponse "couldn't parse user"
Which works, but I know there's a way to avoid the nested pattern matching. I thought this was what bind would do for me, so I tried
parseUser :: ServerPart (Maybe User)
addUser :: User -> ServerPart (Maybe ())
case parseUser >>= saveUser of
Just _ -> success
Nothing -> error
and
savedUser <- (parseUser >>= saveUser)
case savedUser of
Just _ -> success
Nothing -> error
But I get the following error:
Couldn't match type ‘Maybe a0’ with ‘User’
Expected type: Maybe a0 -> ServerPartT IO (Maybe (Key User))
Actual type: User -> ServerPart (Maybe (Key User))
In the second argument of ‘(>>=)’, namely ‘saveUser’
In the expression: parseBody >>= saveUser
which I take to mean >>= is applying saveUser to the Maybe User instead of the User that I need it to, and I'm not sure how to finagle the types to match.
How can I rewrite this to avoid the nested pattern matching?
While I would argue that the original way you have it written is the most readable approach, taking this as an exercise, the MaybeT monad transformer is what you are looking for.
The problem you are running into is that your are trying to jump between the ServerPart monad and the Maybe monad. This is why you can't directly bind parseBody and saveUser. Monad transformers allow you to combine monads to avoid this problem.
import Control.Monad.Trans.Maybe
postUser :: ServerPart Response
postUser = do
-- parseBody :: MaybeT ServerPart User
-- saveUser :: User -> MaybeT ServerPart (Key User)
user <- runMaybeT $ parseBody >>= saveUser
case user of
Just _ -> successResponse
Nothing -> errorResponse "Error saving user"
You will need to refactor your parseBody and saveUser functions to use the MaybeT monad. Since I can't see these functions, I can't help you there, but it can usually be done easily using lift from Control.Applicative.
Useful links on monad transformers:
https://en.wikibooks.org/wiki/Haskell/Monad_transformers
https://www.schoolofhaskell.com/user/commercial/content/monad-transformers
EDIT: MaybeT for parseBody
parseBody :: FromJSON a => MaybeT ServerPart a
parseBody = MaybeT $ fmap A.decode getBody
And just as a general tip: bar >>= (return . f) is equivalent to fmap f bar. The latter being cleaner and more general since it does not require the monad instance.

Catching an Exception from runDb

This is a follow-up to my previous post. MaybeT and Transactions in runDb
I thought this will be a simple thing to do but I have been trying to figure this out for over a day and still haven't made much progress. So thought I will give up and ask!
I just added a try function (from Control.Exception.Lifted) to my previous code and I couldn't get the code to type check. Variants like catch and handle had similar issues.
eauth <- LiftIO (
try( runDb $ do
ma <- runMaybeT $ do
valid <- ...
case ma of
Just a -> return a
Nothing -> liftIO $ throwIO MyException
) :: IO (Either MyException Auth)
)
case eauth of
Right auth -> return auth
Left _ -> lift $ left err400 { errBody = "Could not create user"}
My runDb looks like this (I also tried a variant where I removed liftIO):
runDb query = do
pool <- asks getPool
liftIO $ runSqlPool query pool
I get this error:
No instance for (Control.Monad.Reader.Class.MonadReader Config IO)
arising from a use of ‘runDb’
In the expression: runDb
In the first argument of ‘try’, namely
‘(runDb
$ do { ma <- runMaybeT ...
I am running inside servant handler and my return type is AppM Auth where
type AppM = ReaderT Config (EitherT ServantErr IO)
I have tried many combinations of lifting but doesn't seem to be helping. I thought I will take this opportunity to figure out things from scratch and I hit a wall as well. If someone could suggest how you arrived at the answer, it will be super instructive for me.
This has been my thought process:
I see runSqlConn :: MonadBaseControl IO m => SqlPersistT m a -> Connection -> m a
So that seems to imply it will be in the IO monad, which means try should work
I think check the definition of MonadBaseControl which has class MonadBase b m => MonadBaseControl b m | m -> b. At this point I am confused. This functional dependency logic seems to be suggest type m dictates what b will be but in the previous one b was specified as IO.
I check MonadBase and that did not give me any clue either.
I check SqlPersistT and got no clues either.
I reduced the problem to something very simple like result <- liftIO (try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)) and that worked. So I was even more confused at this time. Doesn't runDb work in IO so shouldn't the same thing work for my original code?
I thought I can figure this out by backtracking but it seems like my level of Haskell knowledge is just not sufficient to get at the root of the problem. Appreciate if people can provide step by step pointers as to arrive at the right solution.
Thanks!
General type signature for try:
(MonadBaseControl IO m, Exception e) => m a -> m (Either e a)
Specialized type signature for try (as it appears in your code):
IO Auth -> IO (Either MyException Auth)
So, the monadic value that is the argument to try has type:
IO Auth
Everything listed above, you probably already understood. If we look at the type signature for your runDb, we get this:
runDb :: (MonadReader Config m, MonadIO m) => SqlPersistT m a -> m a
I sort of had to guess because you didn't provide a type signature, but that is probably what it is. So now, the problem should be a little clearer. You are trying to use runDb to create a monadic value for something that's supposed to be in IO. But IO doesn't satisfy the MonadReader Config instance that you need.
To make the mistake more clear, let's make runDb more monomorphic. You could give it this type signature instead:
type AppM = ReaderT Config (EitherT ServantErr IO)
runDb :: SqlPersistT AppM a -> AppM a
And now if you tried to compile your code, you would get an even better error. Instead of telling you
No instance for (Control.Monad.Reader.Class.MonadReader Config IO)
It would tell you that IO doesn't match AppM (although it would probably expand the type synonym). Practically, what this means is that you can't get the shared pool of database connections magically out of IO. You need the ReaderT Config that was passing it around everywhere.
The easiest fix I can think of would be to stop using exceptions where they aren't necessary:
mauth <- runDb $ runMaybeT $ do
... -- Same stuff you were doing earlier
case mauth of
Just auth -> return auth
Nothing -> lift $ left err400 { errBody = "Could not create user"}

Resources