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.)
Related
Here's a small toy DSL in typed tagless final style (see Typed Tagless Final Interpreters by O. Kiselyov).
class Monad m => RPCToy m where
mkdir :: FilePath -> m ()
ls :: FilePath -> m [FilePath]
The different instatiations of this little DSL would be, for example, the implementation of mkdir and ls on different platforms, either local and remote. Type m is a monad in all implementations, it could be IO, or one provided by some networking library, or some other homebrew monad.
Here's an implementation in IO:
import System.Directory (listDirectory)
import Control.Monad (void)
instance RPCToy IO where
mkdir = void . putStrLn . ("better not create "++)
ls = listDirectory
and a little application
import Control.Monad (unless)
demo :: RPCToy m => m ()
demo = do
files <- ls "."
unless ("test" `elem` files) $
mkdir "test"
that can be run in the IO monad
main :: IO ()
main = do
demo
So far so good.
Now suppose that different implementations rely on the same monad m, e.g. from the same networking library. For the typed tagless final style to work distinct monads are needed, here, that are nonetheless essentially the same. The ambiguity can be removed by wrapping things:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Local a = Local {runLocal :: IO a} deriving (Functor, Applicative, Monad)
and then implement RPCToy Local,
instance RPCToy Local where
mkdir = Local . putStrLn . ("BETTER NOT CREATE "++)
ls = Local . listDirectory
that can be run nicely
main :: IO ()
main = do
runLocal demo
What bugs me is this: The implementers have to put a lot of Locals in their code, or, rather repetitively, wrap the library functions like so
localListDirectory = Local . listDirectory
...
One idea is to create an 'indexed monad' im i a, im i being the monad, that carries around an index type i for the sole purpose of letting the compiler distinguish the different implementations. The RebindableSyntax extension makes this possible without having to give up the do syntax. But each monad needs to be 'lifted' into this indexed monad. The improvement is this: Each monad m and the functions therein need to be lifted only once. Otherwise it's still quite convoluted.
I'm wondering whether there's a nicer way to get rid of the monad wrapping.
Here's one approach: Introduce a monad transformer that just wraps another monad with the twist of adding a phantom type i,
import Control.Monad.Trans.Class (MonadTrans, lift)
newtype IndexedWrapT i m a = IndexedWrapT {runIndexedWrapT :: m a}
deriving (Functor, Applicative, Monad)
instance MonadTrans (IndexedWrapT i) where
lift = IndexedWrapT
The phantom type i has the sole purpose of letting different implementations have distinct type.
Then wrap (lift) the relevant functions once, e.g.:
putStrLn' :: MonadTrans t => String -> t IO ()
putStrLn' = lift . putStrLn
On the implementation side
data MyImpl'
type MyImpl = IndexedWrapT MyImpl' IO
runMyImpl :: MyImpl a -> IO a
runMyImpl = runIndexedWrapT
instance RPCToy MyImpl where
mkdir = putStrLn' . ("BETTER NOT CREATE "++)
....
Having formulated the wrapping operation as a monad transformer it becomes clear that other approaches to composing effects may be used, here, as pointed out in the comments, e.g. freer-simple or polysemy.
I have the following code snippet from internet:
calculateLength :: LengthMonad Int
calculateLength = do
-- all the IO operations have to be lifted to the IO monad in the monad stack
liftIO $ putStrLn "Please enter a non-empty string: "
s <- liftIO getLine
if null s
then throwError "The string was empty!"
else return $ length s
and could not understand, why the author use liftIO?
What is the purpose of liftIO?
It is defined as follows:
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
Is it possible to lift IO a -> [a]? It looks like natural transformation.
IO operations like getLine, putStrLn "..." only work inside the IO monad. Using them inside any other monad will trigger a type error.
Still, there are many monads M which are defined in terms of IO (e.g. StateT Int IO, and apparently your LengthMonad as well) and because of that they allow IO actions to be converted into M-actions, and executed as such.
However, we need a conversion for each M:
convertIOintoM1 :: IO a -> M1 a
convertIOintoM2 :: IO a -> M2 a
convertIOintoM3 :: IO a -> M3 a
...
Since this is cumbersome, the library defines a typeclass MonadIO having such conversion function, so that all the functions above can be named liftIO instead.
In practice, liftIO is used each time one wants to run IO actions in another monad, provided such monad allows for it.
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.
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.
In the code below I manage a game, which owns a list of links.
At each step of the game, I change the game state updating the list of links modified.
As I am learning the State monad, I was trying to apply the State monad technique to this use case.
Nonetheless, at each turn, I need to get a piece of info from IO, using getLine
this gives such a code
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
import Control.Monad
import Control.Monad.State.Strict
import qualified Data.List as List
import qualified Control.Monad.IO.Class as IOClass
type Node = Int
type Link = (Node,Node)
type Links = [Link]
type Gateway = Node
type Gateways = [Gateway]
data Game = Game { nbNodes :: Int, links :: Links, gateways :: Gateways }
computeNextTurn :: State Game Link
computeNextTurn = do
g <- get
inputLine <- IOClass.liftIO getLine -- this line causes problem
let node = read inputLine :: Int
let game#(Game _ ls gs) = g
let linkToSever = computeLinkToSever game node
let ls' = List.delete linkToSever ls
let game' = game{links = ls'}
put game'
return linkToSever
computeAllTurns :: State Game Links
computeAllTurns = do
linkToSever <- computeNextTurn
nextGames <- computeAllTurns
return (linkToSever : nextGames)
computeLinkToSever :: Game -> Node -> Link
computeLinkToSever _ _ = (0,1) -- just a dummy value
-- this function doesnt compute really anything for the moment
-- but it will depend on the value of node i got from IO
However I get an error at compilation:
No instance for (MonadIO Data.Functor.Identity.Identity)
arising from a use of liftIO
and I get the same style of error, if I try to use liftM and lift.
I have read some questions that are suggesting StateT and ST, which I don't grasp yet.
I am wondering if my current techique with a simple State is doomed to fail, and that indeed I can not use State, but StateT / ST ?
Or is there a possible operation to simply get the value from getLine, inside the State monad ?
As #bheklilr said in his comment, you can't use IO from State. The reason for that, basically, is that State (which is just shorthand for StateT over Identity) is no magic, so it's not going to be able to use anything more than
What you can already do in its base monad, Identity
The new operations provided by State itself
However, that first point also hints at the solution: if you change the base monad from Identity to some other monad m, then you get the capability to use the effects provided by m. In your case, by setting m to IO, you're good to go.
Note that if you have parts of your computation that don't need to do IO, but require access to your state, you can still express this fact by making their type something like
foo :: (Monad m) => Bar -> StateT Game m Baz
You can then compose foo with computations in StateT Game IO, but its type also makes it apparent that it can't possibly do any IO (or anything else base monad-specific).
You also mentioned ST in your question as possible solution. ST is not a monad transformer and thus doesn't allow you to import effects from some base monad.