Using the reader monad in snap (or, monad transformers in snap)

756 views Asked by At

Can someone show how to use the snap monad inside the reader monad? Monad transformers confuse me. (Alternatively, I will gladly accept suggestions of tutorials about monad transformers, and ways to see the light and finally grok them.)

Edit: Oops; forgot to specify what I'm actually trying to do, instead of asking for help with a specific thing. Strategy, not tactics. I specifically want to share a database connection/pool amongst all handlers, without having to explicitly pass that database connection/pool when specifying routes. It seems that the reader monad would be the way to accomplish that.

3

There are 3 answers

1
Vagif Verdi On BEST ANSWER

Snap has ApplicationState type that allows you package whatever application wide resources you need (db connections, template engines etc.)

It is located in generated file Application.hs and by default has HeistState and TimerState included into ApplicationState. You can just put your db connection there and it will be available from anywhere in your Snap application.

1
Heatsink On

Assuming the snap monad is from http://hackage.haskell.org/packages/archive/snap-core/0.4.0/doc/html/Snap-Types.html... Snap is a monad (not a monad transformer), so you cannot run it inside an arbitrary monad. You could use the ReaderT transformer to embed Reader functionality inside Snap, if that's what you want.

The type of runSnap is

runSnap :: Snap a -> (ByteString -> IO ()) -> (Int -> IO ()) -> Request -> Iteratee ByteString IO (Request, Response)

which tells us that it runs in an Iteratee ByteString IO monad. A Reader monad does not let you do IO or iterate over an input stream, so you cannot run a Snap computation in a Reader monad.

If you explain what you want to accomplish, someone may be able to suggest a way to achieve it.

3
nominolo On

If you're not afraid to use GHC-specific extensions, here's the no-frills approach to monad transformers:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Control.Monad.Reader

data ReaderData = ...

newtype MyMonad a = MyMonad (ReaderT ReaderData Snap a)
  deriving (Monad, MonadReader ReaderData)

runMyMonad :: MyMonad a -> ReaderData -> Snap a
runMyMonad (MyMonad m) r = runReaderT m r

liftSnap :: Snap a -> MyMonad a
liftSnap act = MyMonad (lift act)

You can now use ask and local to access the reader data. To run an action in the Snap monad, you need to "lift" it into your new monad.

... r <- liftSnap $ ... snap action ...

You may prefer a shorter name, though. So, maybe just snap.