Истинное значение типа Lucid "Term" - PullRequest
2 голосов
/ 04 ноября 2019

Я играл с Haskell, пытаясь создать очень простой сайт, используя Servant и Lucid. На данный момент я достиг стадии «Мой код работает, я понятия не имею, почему». Я попытался создать кнопку Bootstrap. Согласно документу, он должен быть определен как:

<button type="button" class="btn btn-primary">Primary</button>

Итак, я нашел Lucid.Html5 doc: 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», но это мне не сильно помогло.

1 Ответ

4 голосов
/ 04 ноября 2019

Класс типов 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. Почему бы не поставить это прямо тогда? Ну, в некоторых случаях это может помочь вывод типа привода желаемым образом.

...