The App type
data App = App
{ _heist :: Snaplet (Heist App)
, _session :: Snaplet SessionManager
}
The initializer
...
addRoutes [ ("/ss", companyHandler)
, ("", heistServe)
]
...
The handler
companyHandler :: Handler b v ()
companyHandler = method GET getter <|> method POST setter
where
getter = do
value <- getFromSession "name"
writeText $ fromMaybe "nothing" value
setter = do
mname <- getParam "name"
setInSession "name" (convert mname)
getter
convert = T.pack . B.unpack . (fromMaybe "nothing")
The heistServe
has type Handler b (Heist b) ()
Type error:
src/Tutorial.hs:50:52:
Couldn't match type `v' with `SessionManager'
`v' is a rigid type variable bound by
the type signature for companyHandler :: Handler b v ()
at src/Tutorial.hs:50:1
Expected type: Handler b v ()
Actual type: Handler b SessionManager ()
In the second argument of `method', namely `setter'
In the second argument of `(<|>)', namely `method POST setter'
You have to bind your SessionManager
to the context of the handler before you can use functions that operate on it. This is done with:
withTop session $ setInSession "name" (convert mname)
-- Where session is the generated lens for your snaplet
If you also want to commit your session afterwards (because you altered the session and didn't just read a variable), you need to:
withSession . withTop session $ ...
If you add the following piece of code to your main application's snaplet initialization code, you don't have to worry about committing sessions at all, because it is done automatically for you:
wrapHandlers withSession
One way to solve the problem is with the following line:
companyHandler = with session $ method GET getter <|> method POST setter
Another thing I find helpful is to specialize the type signatures to my application. In this case, the new type signature would be:
companyHandler :: Handler App App ()
Most of the time you won't be writing generic code that should be reusable across multiple apps, so the small loss of generality here doesn't hurt you. Having more concrete type signatures generally makes error messages easier to decipher, which can be a big help in getting your code compiling. Even when I am writing code that should be generic across all apps/snaplets, sometimes I still find it helpful to start with a more specific type and then generalize after I get it working.
What dflemstr said about withSession is correct. I typically prefer to do withSession once around everything as opposed to putting it on the call to setInSession, but you should do whatever is appropriate for your application.