I'm currently building a new API, and one of the functions it currently provides is:
inSpan :: Tracer -> Text -> IO a -> IO a
I'm looking to move that Tracer into a monad, giving me a signature more like
inSpan :: MonadTracer m => Text -> m a -> m a
The implementation of inSpan uses bracket, which means I have two main options:
class MonadUnliftIO m => MonadTracer m
or
class MonadMask m => MonadTracer m
But which should I prefer? Note that I'm in control of all the types I've mentioned, which makes me slightly lean towards MonadMask as it doesn't enforce IO at the bottom (that is, we could perhaps have a pure MonadTracer instance).
Is there anything else I should consider?
Let's lay out the options first (repeating some of your question in the process):
MonadMask from the exceptions library. This can work on a wide range of monads and transformers, and does not require that the base monad be IO.
MonadUnliftIO from the unliftio-core (or unliftio) library. This library only works for monads with IO at their base, and which is somehow isomorphic to ReaderT env IO.
MonadBaseControl from the monad-control library. This library will require IO at the base, but will allow non-ReaderT.
Now the tradeoffs. MonadUnliftIO is the newest addition to the fray, and has the least developed library support. This means that, in addition to the limitations of what monads can be instances, many good instances just haven't been written yet.
The important question is: why does MonadUnliftIO make this seemingly arbitrary requirement around ReaderT-like things? This is to prevent issues with lost monadic state. For example, the semantics of bracket_ (put 1) (put 2) (put 3) are not really clear, and therefore MonadUnliftIO disallows a StateT instance.
MonadBaseControl relaxes the ReaderT restriction and has wider library support. It's also considered more complicated internally than the other two, but for your usages that shouldn't really matter. And it allows you to make mistakes with the monadic state as mentioned above. If you're careful in your usage, this won't matter.
MonadMask allows totally pure transformer stacks. I think there's a good argument to be had around the usefulness of modeling asynchronous exceptions in a pure stack, but I understand this kind of approach is something people want to do sometimes. In exchange for getting more instances, you still have the limitations around monadic state, plus the inability to lift some IO control actions, like timeout or forkIO.
My recommendation:
If you want to match the way most people are doing things today, it's probably best to choose MonadMask, it's the most well adopted solution.
If you want that goal, but you also need to do a timeout or withMVar or something, use MonadBaseControl.
And if you know there's a specific set of monads you need compatibility with, and want compile time guarantees about the correctness of your code vis-a-vis monadic state, use MonadUnliftIO.
Related
I have a question about MonadError, namely instead of using MonadError, it is better to use Except?
MonadError and Except are not mutually exclusive. They are things of different kinds.
MonadError is a type class (i.e. an "interface") that defines certain operations (throwError and catchError), which may be implemented by different monads. If a function has a type signature requiring MonadError, it means "I will work in any monad, as long as it implements MonadError, because I make use of the operations defined in it", for example:
f :: MonadError MyErrorType m => Int -> m String
Except, on the other hand, is a specific monad that has a specific underlying implementation. This specific monad happens to implement the operations from MonadError, which means that any function that requires MonadError may be used in Except:
runExcept $ f 42 -- using the definition of `f` above
You could also declare your function to be used specifically in Except, as opposed to "in anything that implements MonadError":
g :: Int -> Except MyErrorType String
Such function can still be used in Except like f can:
runExcept $ g 42
But it cannot be used in other monads that implement MonadError.
The practical consequence of this is that if you're writing some local, small, simple program, it's ok to write all your functions in Except, but if you're writing a bigger system with a lot of modules, or better yet, a reusable library, you're better off writing your functions in MonadError, because then your consumers will have more freedom in consuming your library. In OO world this idea is known as "programming to interface".
One possible drawback of this is that the code might (not always) come out less performant, because the compiler may not be able to do enough inlining without knowing the final types of everything. In practice, however, the performance penalty is trivial in most applications. The usual maxim applies: first measure, then optimize.
So I have this sort of code all over my first serious haskell project:
f :: (MonadTrans t) => ExceptT () (t (StateT A B)) C
f = do mapExceptT lift $ do
lift $ do
...
lift $ do
...
r <- ...
...
return r
>>= \r -> ...
There definitely may be something wrong about how I try to achieve my goals (there might be simpler ways how to do it) but currently I am interested in learning how to handle a stack of monad transformers in some nicer way, if there is one. This is the only way I figured out how to get r in the context of B and lift it to a monad higher in the stack. Lifting whole blocks instead of initial statements is as far as I could get on my own.
What I also often end up with are chains of lift which I found out can be avoided with liftIO if the deep monad is IO. I am not aware of a generic way for other monads though.
Is there a pattern that one can follow when he ends up dealing with such stacks, and having to extract one value at some level, a different value at a different level, combine these and affect any of the two levels or maybe yet another one?
Can the stack be manipulated somehow without neither lifting whole blocks (which causes let and bound variables to be scoped and restricted to the inner block) nor having to lift . lift . ... lift individual actions?
This is a well-known problem with monad transformers in general. Researchers have devised various ways of handling it, none of which is clearly "best". Some of the known solutions include:
The mtl approach, which automatically lifts type classes of monads over its built-in monad transformers (and only its built-in monad transformers). This allows you to just write f :: (MonadState A m, MonadError () m) => m C if those are the only features of the monad that your function is using. Due to its extreme non-portability and a few other reasons, mtl is generally considered pseudo-deprecated. See this page and this question for the gory details.
If you have a particular monad stack you are using over and over again, you can wrap it in a newtype and write instances of the various monad type classes it supports manually. For Functor, Applicative, Monad, and any other type classes implemented by the top-level transformer in your stack, you can use GeneralizedNewtypeDeriving to have the compiler write the instances for you automatically; for other type classes, you will have to insert the appropriate number of lift calls for each method. The advantage of this approach is that it's more general and simpler to understand while giving you the same flexibility at the call site as mtl. The big problem with this approach is that it encourages using a single "mega-monad" for all operations rather than specifying only the needed operations, since adding any new monad transformer to the stack requires writing a whole new list of instances.
In most cases, you don't really want a monad that has "some arbitrary state of type A" and "some arbitrary exception-throwing capability". Rather, the different features offered by your monad stack have some semantic meaning in your mental model of your program. A variation of the previous approach is to create custom type classes for the effects beyond the basic Functor, Applicative, and Monad and write instances for the custom type classes on your newtype'd monad instead. This has a major advantage over the other approaches listed here: you can have a stack with multiple copies of the same monad transformer in it at different positions. This is the strategy I've used the most in my own programs so far.
A completely different approach is effect systems. Normally, an effect system has to be built in to a language's type system, but it's possible to encode an effect system in Haskell's type system. See the effect-monads package.
The usual approach is to use the mtl library rather than using transformers directly. I'm not sure what the story behind your t is, but the usual mtl approach is to use very general type signatures at definition sites, like
foo :: (MonadError e m, MonadState s m) => m Int
Then fix the actual transformer stacks at the call sites. A common recommendation is to wrap up the stack in a newtype to avoid muddying things up where they're used.
If this isn't your style (and it's not for everyone), you can still use the mtl methods to perform operations, while giving an explicit transformer stack. This should cut down on the manual lifting substantially. The advantage of this approach is that it gives you a better view of the interactions of effects at the definition sites; the disadvantage is that more code needs all the information.
I've just been investigating FRP in Haskell (mainly reactive-banana (testimonials!)), and I'm wondering is it common for them, and in case of reactive-banana what's the reason to operate in IO monad instead of being constrained to MonadIO, or rven better, any Monad (so that I could provide own stack)?
It seems to me that's because of focus on GUI programming, where you integrate it with some existing libraries that operate in IO? Would it be feasible to change it so that I could integrate it with custom monad stack?
If you are asking why
reactimate :: Frameworks t => Event t (IO ()) -> Moment t ()
expects an event with values of type IO () instead of allowing for a custom monad M () with instance MonadIO M, then the answer is this:
In practice, custom monad stacks only add state/reader/writer effects to the base IO monad. However, in theory, it is entirely possible to add fancy control mechanism like coroutines or non-determinism. I don't know how to integrate the internal state arising from combinators accumE with these more general effects, and I have no idea what it means for an event to happen in a non-deterministic context. That's why reactimate is restricted to IO.
If you have a custom monad stack that is of the state/reader/writer family, then it is usually possible to map it to a pure IO computation and use this with reactimate. If you find that this doesn't work out (I do think that there might be a problem), I would need a more detailed description of the concrete situation to be able to help.
So I have a project that I think is simple enough to learn with, but complex enough to be interesting that I would like to write using the Happstack library. At it's most fundamental level, this project would just be a fancy file server with some domain-specific REST methods (or whatever, I don't really care if it's truly RESTful or not) for searching and getting said files and metadata. Since I'm also trying to really learn monad transformers right now, I decided this would be the perfect project to learn with. However, I'm running into some difficulties getting it started, particularly with how to construct my transformer stack.
Right now, I'm only worried about a few things: config, error reporting, and state, and logging, so I started with
newtype MyApp a = MyApp {
runMyApp :: ReaderT Config (ErrorT String (StateT AppState IO)) a
} deriving (...)
Since I'm always going to be in IO, I can really easily use hslogger with this to take care of my logging. But I also knew I needed to use ServerPartT in order to interact with Happstack, thus
runMyApp :: ReaderT Config (ErrorT String (StateT AppState (ServerPartT IO))) a
I can get this to run, see requests, etc, but the problem I've run into is that this needs FilterMonad implemented for it in order to use methods like dir, path, and ok, but I have no idea how to implement it for this type. I just need it to pass the filters down to the underlying monad. Can someone give me some pointers on how to get this obviously crucial type class implemented? Or, if I'm just doing something terribly wrong, just steer me in the right direction. I've only been looking at Happstack for a few days, and transformers are still quite new to me. I think I understand them enough to be dangerous, but I don't know enough about them that I could implement one on my own. Any help you can provide is greatly appreciated!
FULL CODE
(X-posted from /r/haskell)
The easiest thing for you to do would be to get rid of ErrorT from your stack. If you look here you can see that Happstack defines built-in passthrough instances of FilterMonad for StateT and ReaderT, but none for ErrorT. If you really want to keep ErrorT in your stack, then you need to write a passthrough instance for it. It will probably look a lot like the one for ReaderT.
instance (FilterMonad res m) => FilterMonad res (ReaderT r m) where
setFilter f = lift $ setFilter f
composeFilter = lift . composeFilter
getFilter = mapReaderT getFilter
I tend to think that you shouldn't have ErrorT baked into your application monad because you won't always be dealing with computations that can fail. If you do need to handle failure in a section of your code, you can always drop into it easily just by wrapping runErrorT around the section and then using ErrorT, lift, and return as needed. Also, extra layer in your transformer stack adds a performance tax on every bind. So while the composability of monad transformers is really nice, you generally want to use them sparingly when performance is a significant consideration.
Also, I would recommend using EitherT instead of ErrorT. That way you can make use of the fantastic errors package. It has a lot of really common convenience functions like hush, just, etc.
Also, if you want to see a real example of what you're trying to do, check out Snap's Handler monad. Your MyApp monad is exactly the problem that snaplets were designed to solve. Handler has some extra complexity because it was designed to solve the problem in a generalized way so that Snap users wouldn't need to build this common transformer stack themselves. But if you look at the underlying implementation you can see that at its core it is really just the Reader and State monads condensed into one.
I'm thinking about ways to use Haskell's type system to enforce modularity in a program. For example, if I have a web application, I'm curious if there's a way to separate all database code from CGI code from filesystem code from pure code.
For example, I'm envisioning a DB monad, so I could write functions like:
countOfUsers :: DB Int
countOfUsers = select "count(*) from users"
I would like it to be impossible to use side effects other than those supported by the DB monad. I am picturing a higher-level monad that would be limited to direct URL handlers and would be able to compose calls to the DB monad and the IO monad.
Is this possible? Is this wise?
Update: I ended up achieving this with Scala instead of Haskell: http://moreindirection.blogspot.com/2011/08/implicit-environment-pattern.html
I am picturing a higher-level monad that would be limited to direct URL handlers and would be able to compose calls to the DB monad and the IO monad.
You can certainly achieve this, and get very strong static guarantees about the separation of the components.
At its simplest, you want a restricted IO monad. Using something like a "tainting" technique, you can create a set of IO operations lifted into a simple wrapper, then use the module system to hide the underlying constructors for the types.
In this way you'll only be able to run CGI code in a CGI context, and DB code in a DB context. There are many examples on Hackage.
Another way is to construct an interpreter for the actions, and then use data constructors to describe each primitive operation you wish. The operations should still form a monad, and you can use do-notation, but you'll instead be building a data structure that describes the actions to run, which you then execute in a controlled way via an interpreter.
This gives you perhaps more introspection than you need in typical cases, but the approach does give you full power to insspect user code before you execute it.
I think there's a third way beyond the two Don Stewart mentioned, which may even be simpler:
class Monad m => MonadDB m where
someDBop1 :: String -> m ()
someDBop2 :: String -> m [String]
class Monad m => MonadCGI m where
someCGIop1 :: ...
someCGIop2 :: ...
functionWithOnlyDBEffects :: MonadDB m => Foo -> Bar -> m ()
functionWithOnlyDBEffects = ...
functionWithDBandCGIEffects :: (MonadDB m, MonadCGI m) => Baz -> Quux -> m ()
functionWithDBandCGIEffects = ...
instance MonadDB IO where
someDBop1 = ...
someDBop2 = ...
instance MonadCGI IO where
someCGIop1 = ...
someCGIop2 = ...
The idea is very simply that you define type classes for the various subsets of operations you want to separate out, and then parametrize your functions using them. Even if the only concrete monad you ever make an instance of the classes is IO, the functions parametrized on any MonadDB will still only be allowed to use MonadDB operations (and ones built from them), so you achieve the desired result. And in a "can do anything" function in the IO monad, you can use MonadDB and MonadCGI operations seamlessly, because IO is an instance.
(Of course, you can define other instances if you want to. Ones to lift the operations through various monad transformers would be straightforward, and I think there's actually nothing stopping you from writing instances for the "wrapper" and "interpreter" monads Don Stewart mentions, thereby combining the approaches - although I'm not sure if there's a reason you would want to.)
Thanks for this question!
I did some work on a client/server web framework that used monads to distinguish between different exection environments. The obvious ones were client-side and server-side, but it also allowed you to write both-side code (which could run on both client and server, because it didn't contain any special features) and also asynchronous client-side which was used for writing non-blocking code on the client (essentially a continuation monad on the client-side). This sounds quite related to your idea of distinguishing between CGI code and DB code.
Here are some resources about my project:
Slides from a presentation that I did about the project
Draft paper that I wrote with Don Syme
And I also wrote my Bachelor thesis on this subject (which is quite long though)
I think this is an interesting approach and it can give you interesting guarantees about the code. There are some tricky questions though. If you have a server-side function that takes an int and returns int, then what should be the type of this function? In my project, I used int -> int server (but it may be also possible to use server (int -> int).
If you have a couple of functions like this, then it isn't as straightforward to compose them. Instead of writing goo (foo (bar 1)), you need to write the following code:
do b <- bar 1
f <- foo b
return goo f
You could write the same thing using some combinators, but my point is that composition is a bit less elegant.