How to use persistent in a monad stack? - haskell

I have been enjoying learning Haskell and I think I am making some good progress with the help of folks here and at #haskell. My learning is mostly still at the point where I look at examples and try to abstract out the techniques applied there and apply them to my own code.
Currently, I have started looking at developing monad stacks for various applications and I am looking to incorporate the functionality of the persistent framework into my application.
Here is my monad stack:
newtype App a = App { unApp :: StateT AppState (SqlPersistT (ResourceT (LoggingT IO))) a }
deriving ( Applicative
, Functor
, Monad
, MonadIO
, MonadState AppState
)
AppState is just a record data type holding a single Int value in this example.
My main function looks like:
main = runApp "./test.sqlite" (AppState 69) runMigrate
where runApp is supposed to unwrap all the monads:
runApp :: Text -> AppState -> App a -> IO a
runApp t s a =
runStdoutLoggingT . runResourceT . withSqliteConn t . runSqlConn . flip evalStateT s . unApp
and runMigrate is the application to run in the App monad. In this case I was shooting for just getting it to run the migration:
runMigrate :: App ()
runMigrate = return $ liftPersist $ runMigration migrateAll
The compiler points out that I don't know what I am doing with the complaint:
Main.lhs:59:16:
Couldn't match type ‘m0 ()’ with ‘()’
Expected type: App ()
Actual type: App (m0 ())
In the expression: return $ liftPersist $ runMigration migrateAll
In an equation for ‘runMigrate’:
runMigrate = return $ liftPersist $ runMigration migrateAll
Questions:
What is the right way to do this?
What happens if in my monad stack I introduce a ReaderT? Given that SqlPersistT is really a ReaderT how can I make sure that ask is matched to the real ReaderT and not SqlPersistT?

For your first question: return isn't the right function --- the point of return is that return x does none of the work of your monad and just returns a value. I think you probably want:
runMigrate = App $ lift $ runMigration migrateAll
App lifts your newtype definition into your monad; lift lifts the PersistT into the wrapping StateT.
(Incidentally, I recommend naming App . lift . runMigration as something like runMigrationApp if you're going to be using it a lot.)

Related

Haskell Monads and the liftIO I don't get it

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.

Avoid `unsafePerformIO` for interaction in a Process Monad

I am using Cloud Haskell for message processing. I am also using a general monad transformer stack (with IO at the bottom) for some general tracking of state, configuration etc.
I am running into a situation where I have to use unsafePerformIO for working inside the Process monad. I am describing the situation below. Please bear in mind this is a much contrived example to simplify and present the crux of the problem
data AppConfig
data AppState
type App = ReaderT AppConfig (StateT AppState IO)
runApp :: App a -> Int -> IO (a, AppState)
runApp k maxDepth =
let config = AppConfig maxDepth
state = AppState 0
in runStateT (runReaderT k config) state
msgHandler :: App ()
msgHandler = -- some message handling logic here --
runServer :: Process ()
runServer = do
let run handler = return $ unsafePerformIO $ runApp handler
(_,_) <- receiveWait [match $ run taskSubmissionHandler]
runServer
Can this unsafePerformIO be somehow avoided? I understand that the Process monad itself is just a wrapper around the IO monad but there are certain IO operations which are essential inside my transformer stack, which cannot be avoided.
Yes, sure. Process is an instance of MonadIO, so you can just
let run = liftIO . runApp
instead.

Is there a better way than writing `lift`s before each `IO` in a nested `MonadTrans`?

Sometimes I need to use several nested MonadTrans. For example, I would put a MaybeT inside of a ExceptT, to mimick continue and break in imperative programming:
runExceptT . forM [1..] $ \ _ -> runMaybeT $ do
...
mzero -- this mimics continue
lift $ throwE "..." -- this mimics break
lift . lift $ putStrLn "Hello!"
...
However, as the above code shows, every time I need to do any IO inside of this "artificial loop", I need to put an ugly lift . lift before it. Imagine if I have even more complicated nestings, and a lot of IO operations, this quickly becomes an anonyance. How I make the code cleaner and more concise?
For the particular case of IO, you can use liftIO :: MonadIO m => IO a -> m a to lift an IO action through an arbitrarily deep monad transformer stack:
liftIO $ putStrLn "Hello"
This expression has type MonadIO m => m (). Any transformer stack that contains an instance of MonadIO somewhere inside of it should have a MonadIO instance itself, which is why this works for a stack of any depth.

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"}

How to create a Database Monad Stack in Happstack?

I want to create a Happstack application with lots of access to a database. I think that a Monad Stack with IO at the bottom and a Database Write-like monad on top (with log writer in the middle) will work to have a clear functions in each access, example:
itemsRequest :: ServerConfig -> ServerPart Response
itemsRequest cf = dir "items" $ do
methodM [GET,HEAD]
liftIO $ noticeM (scLogger cf) "sended job list"
items <- runDBMonad (scDBConnString cf) $ getItemLists
case items of
(Right xs) -> ok $ toResponse $ show xs
(Left err) -> internalServerError $ toResponse $ show err
With:
getItemList :: MyDBMonad (Error [Item])
getItemList = do
-- etc...
But I have little knowledge of Monad and Monad Transformers (I see this question as an exercise to learn about it), and I have no idea how to begin the creation of Database Monad, how to lift the IO from happstack to the Database Stack,...etc.
Here is some minimal working code compiled from snippets above for confused newbies like me.
You put stuff into AppConfig type and grab it with ask inside your response makers.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Happstack.Server
import Control.Monad.Reader
import qualified Data.ByteString.Char8 as C
myApp :: AppMonad Response
myApp = do
-- access app config. look mom, no lift!
test <- ask
-- try some happstack funs. no lift either.
rq <- askRq
bs <- lookBS "lol"
-- test IO please ignore
liftIO . print $ test
liftIO . print $ rq
liftIO . print $ bs
-- bye
ok $ toResponse ("Oh, hi!" :: C.ByteString)
-- Put your stuff here.
data AppConfig = AppConfig { appSpam :: C.ByteString
, appEggs :: [C.ByteString] } deriving (Eq, Show)
config = AppConfig "THIS. IS. SPAAAAAM!!1" []
type AppMonad = ReaderT AppConfig (ServerPartT IO)
main = simpleHTTP (nullConf {port=8001}) $ runReaderT myApp config {appEggs=["red", "gold", "green"]}
You likely want to use 'ReaderT':
type MyMonad a = ReaderT DbHandle ServerPart a
The Reader monad transformer makes a single value accessible using the ask function - in this case, the value we want everyone to get at is the database connection.
Here, DbHandle is some connection to your database.
Because 'ReaderT' is already an instance of all of the happstack-server type-classes all normal happstack-server functions will work in this monad.
You probably also want some sort of helper to open and close the database connection:
runMyMonad :: String -> MyMonad a -> ServerPart a
runMyMonad connectionString m = do
db <- liftIO $ connect_to_your_db connectionString
result <- runReaderT m db
liftIO $ close_your_db_connection db
(It might be better to use a function like 'bracket' here, but I don't know that there is such an operation for the ServerPart monad)
I don't know how you want to do logging - how do you plan to interact with your log-file? Something like:
type MyMonad a = ReaderT (DbHandle, LogHandle) ServerPart a
and then:
askDb :: MyMonad DbHandle
askDb = fst <$> ask
askLogger :: MyMonad LogHandle
askLogger = snd <$> ask
might be enough. You could then build on those primitives to make higher-level functions. You would also need to change runMyMonad to be passed in a LogHandle, whatever that is.
Once you get more than two things you want access to it pays to have a proper record type instead of a tuple.

Resources