1

应用类型

data App = App
    { _heist       :: Snaplet (Heist App)
    , _session     :: Snaplet SessionManager
    }

初始化器

...
addRoutes [ ("/ss", companyHandler)
          , ("", heistServe)
          ]
...

处理程序

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")

heistServe类型Handler b (Heist b) ()

类型错误:

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'
4

2 回答 2

3

您必须SessionManager先将您绑定到处理程序的上下文,然后才能使用对其进行操作的函数。这是通过以下方式完成的:

withTop session $ setInSession "name" (convert mname)
-- Where session is the generated lens for your snaplet

如果您还想在之后提交会话(因为您更改了会话并且不只是读取变量),您需要:

withSession . withTop session $ ...

如果将以下代码添加到主应用程序的 snaplet 初始化代码中,则根本不必担心提交会话,因为它会自动为您完成:

wrapHandlers withSession
于 2011-12-12T15:08:47.890 回答
0

解决问题的一种方法是使用以下行:

companyHandler = with session $ method GET getter <|> method POST setter

我发现有帮助的另一件事是将类型签名专门用于我的应用程序。在这种情况下,新的类型签名将是:

companyHandler :: Handler App App ()

大多数情况下,您不会编写应该可跨多个应用程序重用的通用代码,因此这里的一般性小损失不会伤害您。拥有更具体的类型签名通常会使错误消息更容易解读,这对编译代码有很大帮助。即使我编写的代码应该在所有应用程序/snaplet 中通用,有时我仍然发现从更具体的类型开始,然后在我得到它工作后进行概括是有帮助的。

dflemstr 关于 withSession 的说法是正确的。我通常更喜欢在所有事情上都使用一次 withSession,而不是把它放在对 setInSession 的调用上,但是你应该做任何适合你的应用程序的事情。

于 2011-12-19T17:08:12.207 回答