Type error using snaplet-session

2019-07-29 10:34发布

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'

2条回答
Juvenile、少年°
2楼-- · 2019-07-29 10:58

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.

查看更多
霸刀☆藐视天下
3楼-- · 2019-07-29 11:11

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
查看更多
登录 后发表回答