2

我一直在玩 Haskell,尝试使用 Servant 和 Lucid 创建一个非常简单的网站。此刻我达到了“我的代码有效,我不知道为什么”的阶段。我尝试创建引导按钮。根据文档,它应该定义为:

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

它必须对“Term”类型类做一些事情,但我不知道如何理解它。有人可以帮我弄这个吗 ?我尝试将模块加载到 ghci 并使用“:t”检查类型,但这对我没有太大帮助。

4

1 回答 1

4

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

这是用于创建没有属性的元素。我们arg作为参数传递给term代表孩子的一段 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。那为什么不直接放呢?好吧,在某些情况下,它可以帮助以理想的方式推动类型推断

于 2019-11-04T07:09:07.127 回答