Pregunta

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)

¿Fue útil?

Solución

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top