2

我想使用一个网站作为一个工作示例来帮助学习 Haskell。我正在尝试遵循 Snap 网站上的Heist 教程,并在网页中显示阶乘函数的结果。

我可以在编译器不抱怨的情况下获得定义为“服务器端”的示例函数,但我无法弄清楚如何将该函数绑定到一个标记,然后我可以将其放入 HTML 中。具体来说,这部分工作正常(例如,在 Site.hs 中):

factSplice :: Splice Snap
factSplice = do
    input <- getParamNode
    let text = T.unpack $ X.nodeText input
        n = read text :: Int
    return [X.TextNode $ T.pack $ show $ product [1..n]]

但是真正重要的部分——如何评估这个函数作为网页的一部分(例如如何将它绑定到像 <fact /> 这样的标签)——是神秘的。说明说要放弃:

bindSplice "fact" factSplice templateState

代码中的某处。但仅此还不够。该语句不是表达式(stuff = bindSplice...),因此不清楚如何其放在代码中的位置。此外,“templateState”应该来自哪里完全不清楚。似乎“templateState”应该是默认值的占位符,例如emptyTemplateStateor defaultHeistState,但这些似乎在几年前就已被弃用,最新版本的 Heist (0.14) 无法识别它们。

MightyByte 在 2011 年曾多次评论过此类问题,但答案都掩盖了令人困惑的部分,即如何将数据实际获取到网页中。任何人都可以帮忙吗?

- 更新 -

非常感谢你,mightybyte!您的解释和对源代码的一些粗略探究消除了很多混乱,我能够从 Snap 网站教程中获得阶乘示例。这是我的解决方案 - 我是一个完整的 n00b,如果解释看起来迂腐或明显,请道歉。

我或多或少使用了 mightybyte 建议的 addConfig 方法,只是addAuthSplicesSpliceHelpers.hs复制了实现。我通过“snap init”从默认项目开始,并addMySplices在 Site.hs 中定义了一个函数

addMySplices :: HasHeist b => Snaplet (Heist b) -> Initializer b v ()
addMySplices h = addConfig h sc
    where
      sc = mempty & scInterpretedSplices .~ is
      is = do
           "fact" ## factSplice

这使用镜头来访问 SpliceConfig 中性元素的字段mempty,因此我必须添加Control.Lens到 Site.hs 中的依赖项,以及Data.Monoid放入mempty范围。我还将阶乘拼接的类型签名更改为factSplice :: Monad n => I.Splice n,但该函数与 Heist 教程中的形式保持不变。然后我在 Site.hsaddMySplices旁边的应用程序初始化程序中调用addAuthSplices

app :: SnapletInit App App
...
addAuthSplices h auth
addMySplices h
...

这导致factSplice被绑定到 tag <fact><fact>8</fact>正如所宣传的那样,放入默认模板之一会在页面上呈现 40320 。

大约一年前的这个问题包含一个表面上相似的解决方案,但不适用于最新版本的 Heist;不同之处在于,某些字段是通过镜头而不是直接访问的,这在 Snap 项目博客中的 Heist 0.14 公告中进行了解释。特别是,hcCompliedSplices已经完全重新定义 - 在 Types.hs 中甚至有一个友好的警告。

4

1 回答 1

0

如果您查看顶级 Heist 模块,您会看到initHeist 函数。正如文档所指出的,这是主要的初始化函数。你应该把你所有的模板和拼接都传递给它。查看类型签名告诉我们这些都捆绑在 HeistConfig 数据类型中。

initHeist :: Monad n => HeistConfig n -> EitherT [String] IO (HeistState n)

renderTemplate这会给你一个 HeistState,当你想要渲染一个模板时,它是你传递给的。此外,HeistConfig 包含所有拼接的 SpliceConfig 字段。

但这都是低级接口。Heist 被设计为不依赖于 Snap。它是一个模板库,可以与任何 Web 框架一起使用。(或者甚至是独立的,根本没有用于生成 HTML 电子邮件之类的 Web 框架。)如果您将 Heist 与 Snap 一起使用,您可能希望使用我们提供的方便的东西来处理调用 initHeist、renderTemplate、等等

对于一个工作示例,最好的办法是查看执行“snap init”时获得的项目模板。你可以在这里看到github上的代码。如果您查看Application.hs,您会看到应用程序数据结构中有一行包含 Heist snaplet。然后,如果您查看Site.hs 的底部,您会看到有一个对 heistInit 的调用。此功能由heist snaplet定义。snaplet 为您处理初始化、状态管理、动态模板重新加载等。

所以,把这一切放在一起……从上面描述的 Heist API 中,我们看到我们需要在 HeistConfig 中定义我们的拼接。但是我们的应用程序将主要通过 snaplet 的 API 与所有这些东西进行交互。因此,如果我们在抢劫 snaplet API中查找涉及 HeistConfig/SpliceConfig 的内容,我们会发现两个相关函数:

heistInit' :: FilePath -> HeistConfig (Handler b b) -> SnapletInit b (Heist b)
addConfig :: Snaplet (Heist b) -> SpliceConfig (Handler b b) -> Initializer b v ()

这些类型签名提出了两种定义拼接的方法。您可以使用heistInit'而不是heistInit将您的 HeistConfig 预先传递给它。addConfig或者您可以通过调用结果来添加拼接Snaplet (Heist b)

于 2014-11-05T20:58:49.293 回答