我一直在玩Haskell,试图使用Servant和Lucid创建一个非常简单的网站。当我到达"我的代码工作,我不知道为什么"的阶段。我试着创建Bootstrap按钮。根据文档,应将其定义为:
<button type="button" class="btn btn-primary">Primary</button>
所以我找到了Lucid.Html5文档:https://hackage.haskell.org/package/lucid-2.9.11/docs/Lucid-Html5.html并计算出创建按钮的功能:
button_ :: Term arg result => arg -> result
在花了一些时间试图找出正确的语法后,我想出了这个:
-- correctly replicates the html pasted above
button_ [type_ "button", class_ "btn btn-primary"] "Primary"
通常我会称之为胜利,并专注于其他任务,但这对我来说是一个真正的魔术
文档中说"button_"是一个接受参数"arg"并返回泛型类型"result"的值的函数。然而,在我的应用程序中,"button_"显然接受两个参数并返回"Html(("。
-- f arg arg again ??
button_ [type_ "button", class_ "btn btn-primary"] "Primary"
它一定与"术语"类型类有关,但我不知道如何理解它。有人能帮我吗?我试着将模块加载到ghci中,并用":t"检查类型,但这对我没有太大帮助。
Term
类型类非常方便——我们不需要不同的term
函数来创建带有或不带有属性的元素——但可能有点难以理解。
button_
的定义是
-- | @button@ element
button_ :: Term arg result => arg -> result
button_ = term "button"
term
是Term
类型类的一种方法,类型为:
term :: Text -> arg -> result
也就是说:你给它元素的名称,某个参数的类型取决于特定的实例,它返回某个结果,其类型取决于该特定的实例。但有哪些实例可用?有三种:
Term Text Attribute
-- here, term :: Text -> Text -> Attribute
这个用于创建属性,而不是元素。
Applicative m => Term (HtmlT m a) (HtmlT m a)
-- here, term :: Text -> HtmlT m a -> HtmlT m a
这个用于创建没有属性的元素。我们作为参数传递给term
的arg
是表示子级的一段html,作为回报,我们得到另一段html。
(Applicative m, f ~ HtmlT m a) => Term [Attribute] (f -> HtmlT m a)
-- here, term :: Text -> [Attribute] -> HtmlT m a -> HtmlT m a
这一个是最令人困惑的,也是您的代码中正在使用的一个。这里,arg
是Attribute
值的列表。这一点很清楚。但是result
是函数的类型!传递属性后,我们剩下另一个函数HtmlT m a
->HtmlT m a
,它允许我们提供按钮的内容(在您的情况下为"主要"(。
f ~ HtmlT m a
是另一个与这个答案并不相关的问题。它简单地说f
等于HtmlT m a
。那为什么不直接说呢?在某些情况下,它可以帮助以理想的方式驱动类型推理。