Is Yesod's Handler Monad an instance of MonadBaseControl IO? - haskell

I would like to use the withResource :: MonadBaseControl IO m => Pool a -> (a -> m b) -> m b from the Data.Pool library in my handler code. I am trying to determine if Handler is an instance of MonadBaseControl IO, but I'm having a hard time finding the exact place that Handler is defined.
Is Yesod's Handler Monad (as created by mkYesodData) an instance of MonadBaseControl IO or can it easily be made into one?

No, Handler does not have a MonadBaseControl IO instance.
However, it's basically a reader, so it's not too difficult to write one.
Assuming you're using the scaffolded site, the following should work. Add some extensions and imports to the top of Foundation:
import Control.Monad.Base
import Control.Monad.Trans.Control
import Yesod.Core.Types (HandlerFor(..))
In some convenient place after the mkYesodData statement, add the instances:
instance MonadBase IO Handler where
liftBase = liftIO
instance MonadBaseControl IO Handler where
type StM Handler a = a
liftBaseWith ioAct = HandlerFor $ \handlerData ->
ioAct (\handlerAct -> unHandlerFor handlerAct handlerData)
restoreM = return
I have no idea if what you're generally trying to do is a good idea or not, but that should get things to type-check.

Related

Monad and MonadIO for custom type

I have a Logger type of kind * -> * which can take any type and log the value in a file. I am trying to implement this in a monadic way so that I log and keep working the same. My code looks like
import Control.Applicative
import Control.Monad
import System.IO
import Control.Monad.IO.Class
instance Functor Logger where
fmap = liftM
instance Applicative Logger where
pure = return
(<*>) = ap
newtype Logger a = Logger a deriving (Show)
instance Monad (Logger) where
return = Logger
Logger logStr >>= f = f logStr
instance MonadIO (Logger) where
liftIO a = do
b <- liftIO a
return b
logContent :: (Show a) => a -> Logger a
logContent a = do
b <- liftIO $ logContent2 a
return b
logContent2 :: (Show a) => a -> IO a
logContent2 a = do
fHandle <- openFile "test.log" AppendMode
hPrint fHandle a
hClose fHandle
return (a)
The liftIO function goes on endless loop as it calls itself. I am not able to do b <- a either. Can someone help on getting MonadIO implementation right ?
As noted in the comments, I think you've misunderstood what MonadIO and liftIO do.
These typeclasses and functions come from mtl library. Rather unfortunately, mtl stands for "monad transformer library", but mtl is not a monad transformer library. Rather, mtl is a set of typeclasses that allow you to take a monad that --- and this is important --- already has a particular type of functionality and provide that monad with a consistent interface around that functionality. This ends up being really useful for working with actual monad transformers. That's because mtl allows you to use tell and ask and put to access the Writer, Reader, and State functionality of your monad transformer stack in a consistent way.
Separately from this transformer business, if you already have a custom monad, say that supports arbitrary IO and has State functionality, then you can define a MonadState instance to make the standard state operations (state, get, gets, put, modify) available for your custom monad, and you can define a MonadIO instance to allow an arbitrary IO action to be executed in your custom monad using liftIO. However, none of these typeclasses are capable of adding functionality to a monad that it doesn't already have. In particular, you can't transform an arbitrary monadic action m a into an IO a using a MonadIO instance.
Note that the transformers package contains types that are capable of adding functionality to a monad that it doesn't already have (e.g., adding reader or writer functionality), but there is no transformer to add IO to an arbitrary monad. Such a transformer would be impossible (without unsafe or nonterminating operations).
Also note that the signature for liftIO :: MonadIO m => IO a -> m a puts a MonadIO constraint on m, and this isn't just a trivial constraint. It actually indicates that liftIO only works for monads m that already have IO functionality, so either m is the IO monad, or it's a monad stack with IO at its base. Your Logger example doesn't have IO functionality and so can't have a (sensible) MonadIO instance.
Getting back to your specific problem, it's actually a little bit hard to steer you right here without knowing exactly what you're trying to do. If you just want to add file-based logging to an existing IO computation, then defining a new transformer stack will probably do the trick:
type LogIO = ReaderT Handle IO
logger :: (Show a) => a -> LogIO ()
logger a = do
h <- ask
liftIO $ hPrint h a
runLogIO :: LogIO a -> FilePath -> IO a
runLogIO act fp = withFile fp AppendMode $ \h -> runReaderT act h
and you can write things like:
main :: IO ()
main = runLogIO start "test.log"
start :: LogIO ()
start = do
logger "Starting program"
liftIO . putStrLn $ "Please enter your name:"
n <- liftIO $ getLine
logger n
liftIO . putStrLn $ "Hello, " ++ n
logger "Ending program"
The need to add liftIO calls when using IO actions within the LogIO monad is ugly but largely unavoidable.
This solution would also work for adding file-based logging to pure computations, with the understanding that you have to convert them to IO computations anyway if you want to safely log to a file.
The more general solution is to define your own monad transformer (not merely your own monad), like LoggerT m, together with an associated MonadLogger type class that will add file-based logging to to any IO-capable monad stack. The idea would be that you could then create arbitrary custom monad stacks:
type MyMonad = StateT Int (LoggerT IO)
and then write code that mixes monadic computations from different layers (like mixing state computations and file-based logging):
newSym :: String -> MyMonad String
newSym pfx = do
n <- get
logger (pfx, n)
put (n+1)
return $ pfx ++ show n
Is this what you what you're trying to do? If not, maybe you could describe, either here or in a new question, how you're trying to add logging to some example code.

Combining Persistents SqlPersistT with Servants Handler

I'm trying to define a helper function that will allow me to run queries using a connection pool provided when run in the Reader monad. I would like to define a function that will abstract away the asks part of fetching the pool, like so:
type Config = Config { getPool :: ConnectionPool }
type App = ReaderT Config
runQuery :: MonadBaseControl IO m => SqlPersistT m a -> App m a
runQuery query =
asks getPool >>= lift . runSqlQuery
Though I am having trouble getting this to compile... I could have sworn this code had worked on a previous project, and low and behold it had. When I went to look at the definitions of Handler (the m I want in App m), and runSqlQuery they both differ. It seems that Handler has an instance for MonadBaseControl IO m whereas runSqlQuery uses MonadUnliftIO, however, looking back to the change prior to when MonadUnliftIO was added, runSqlQuery used MonadBaseControl IO m... which I presume is why my previous code worked in this instance.
So I suppose my question is, how can I change my implementation of runQuery to support the fact that runSqlQuery is based off of MonadUnliftIO now? I had a look through the functions MonadUnliftIO provides (namely unliftIO) but I have been unable to figure out a way to make the types fit.
It seems this may be an intentional design descision of MonadUnliftIO, looking at the readme here https://www.stackage.org/package/unliftio under 'Limitations' it states that it does not support "Transformers with multiple exit points (e.g., ExceptT)", and Servant's Handler is a wrapper around ExceptT. I may just need to find another way of writing this code.
This compiles with persistent-2.8.2:
runQuery :: MonadUnliftIO m => ReaderT SqlBackend m b -> App m b
runQuery query =
asks getPool >>= lift . runSqlPool query

What is the best way to manage resources in a monad stack like ExceptT a IO?

For better or for worse, Haskell's popular Servant library has made it common-place to run code in a monad transformer stack involving ExceptT err IO. Servant's own handler monad is ExceptT ServantErr IO. As many argue, this is a somewhat troublesome monad to work in since there are multiple ways for failure to unroll: 1) via normal exceptions from IO at the base, or 2) by returning Left.
As Ed Kmett's exceptions library helpfully clarifies:
Continuation-based monads, and stacks such as ErrorT e IO which provide for multiple failure modes, are invalid instances of this [MonadMask] class.
This is very inconvenient since MonadMask gives us access the helpful [polymorphic version of] bracket function for doing resource management (not leaking resources due to an exception, etc.). But in Servant's Handler monad we can't use it.
I'm not very familiar with it, but some people say that the solution is to use monad-control and it's many partner libraries like lifted-base and lifted-async to give your monad access to resource management tools like bracket (presumably this works for ExceptT err IO and friends as well?).
However, it seems that monad-control is losing favor in the community, yet I can't tell what the alternative would be. Even Snoyman's recent safe-exceptions library uses Kmett's exceptions library and avoids monad-control.
Can someone clarify the current story for people like me who are trying to plow our way into serious Haskell usage?
You could work in IO, return a value of type IO (Either ServantErr r) at the end and wrap it in ExceptT to make it fit the handler type. This would let you use bracket normally in IO. One problem with this approach is that you lose the "automatic error management" that ExceptT provides. That is, if you fail in the middle of the handler you'll have to perform an explicit pattern match on the Either and things like that.
The above is basically reimplementing the MonadTransControl instance for ExceptT, which is
instance MonadTransControl (ExceptT e) where
type StT (ExceptT e) a = Either e a
liftWith f = ExceptT $ liftM return $ f $ runExceptT
restoreT = ExceptT
monad-control works fine when lifting functions like bracket, but it has odd corner cases with functions like the following (taken from this blog post):
import Control.Monad.Trans.Control
callTwice :: IO a -> IO a
callTwice action = action >> action
callTwice' :: ExceptT () IO () -> ExceptT () IO ()
callTwice' = liftBaseOp_ callTwice
If we pass to callTwice' an action that prints something and fails immediately after
main :: IO ()
main = do
let printAndFail = lift (putStrLn "foo") >> throwE ()
runExceptT (callTwice' printAndFail) >>= print
It prints "foo" two times anyway, even if our intuition says that it should stop after the first execution of the action fails.
An alternative approach is to use the resourcet library and work in a ExceptT ServantErr (ResourceT IO) r monad. You would need to use resourcet functions like allocate instead of bracket, and adapt the monad at the end like:
import Control.Monad.Trans.Resource
import Control.Monad.Trans.Except
adapt :: ExceptT ServantErr (ResourceT IO) r -> ExceptT err IO r
adapt = ExceptT . runResourceT . runExceptT
or like:
import Control.Monad.Morph
adapt' :: ExceptT err (ResourceT IO) r -> ExceptT err IO r
adapt' = hoist runResourceT
My recommendation: have your code live in IO instead of ExceptT, and wrap each handler function in a ExceptT . try.

Monad type mismatch

I am attempting to use the IO monad in a Spock application. The following code does not compile:
get "api/entities" $ do
entities <- loadEntities
let e1 : xs = entities
text $ note e1
loadEntities has the type IO [Entity]
The error is Couldn't match type ‘ActionT IO ()’ with ‘t0 -> IO b0’
Is Spock using a monad other than IO? If so, how do I get the result of loadEntities?
Writing answer so people can see a better explanation.
If you look at get it has the type.
get :: MonadIO m => SpockRoute -> ActionT m () -> SpockT m ()
ActionT is a monad transformer and it requires in inner monad to have an instance of MonadIO. If we look at the typeclass MonadIO it has a function
liftIO :: IO a -> m a
This means every that you call execute functions of the type IO a in that stack by using liftIO. ActionT has an instance of MonadIO also and which is the one you are using here to call your IO function. So to call loadEntities you had to
entities <- liftIO loadEntities
If you end of calling a certain function like that a lot you can create a separate module import it qualified and export a more friendly one.
module Lifted (loadEntities) where
import qualified SomeModule as IO
loadEntities :: MonadIO m => m Entities
loadEntities = liftIO IO.loadEntities
This will make it so you don't always have you use liftIO

How to use ReaderT to transform Happstack's ServerPart Response?

This is the first time I'm playing with Monad Transformers. This is a simple happstack app.
{-# LANGUAGE OverloadedStrings #-}
import Happstack.Lite
import qualified Data.ByteString.Lazy.Char8 as L
main :: IO ()
main = do
serve Nothing hello
hello :: ServerPart Response
hello = do
ok $ toResponse ("Hello" :: L.ByteString)
I would like to be able to change hello so it can read some global config data using ReaderT. Let's just say the config is a string to keep it simple
type NewMonad = ReaderT L.ByteString (ServerPartT IO)
runNewMonad :: NewMonad a -> L.ByteString -> ServerPart a
runNewMonad k c = runReaderT k c
How do I change hello so it can use ask? I'm not sure what the type would be. NewMonad Response isn't quite right, because ok returns a ServerPart Response.
How do I change main so that serve works? It expects a ServerPart Response.
In fact, NewMonad Response is the correct type for hello; you just need to use lift to transform an action in the underlying monad to one in the transformer. For example:
hello :: NewMonad Response
hello = do
foo <- ask
lift . ok $ toResponse foo
In general,
lift :: (MonadTrans t, Monad m) => m a -> t m a
i.e., if you have a monadic action, then you turn it into an action in any monad transformer over that monad. This is the definition of a monad transformer: it can transform over any monad, and embed actions of that monad.
It seems that restricting all the monadic actions to one specific monad — rather than using typeclasses to work in any appropriate monad — is one of the simplifications happstack-lite uses compared to the full Happstack, which has this type for ok:
ok :: (FilterMonad Response m) => a -> m a
With this type, assuming appropriate instances are declared for the standard transformers, you could just use ok directly in MyMonad.
As for main, you need to eliminate the ReaderT layer, leading a a ServerPart Response that you can pass to serve:
main :: IO ()
main = do
serve Nothing $ runNewMonad hello ("Hello" :: L.ByteString)
(This would cause problems if you were using a monad carrying state that you wanted to change over the course of many requests, since serve's type is too restrictive to support such state threading (without manually encoding it with IORefs or similar); possibly the unrestricted Happstack has the ability to do this, but it'd likely be very brittle anyway, as you shouldn't really be relying on the order requests are processed in like that.)

Resources