I've been playing with Haskell, trying to create a very simple website using Servant and Lucid. At the moment I reached the stage "My code works, I have no idea why". I tried creating Bootstrap button. According the the doc, it should be defined as:
<button type="button" class="btn btn-primary">Primary</button>
So I found Lucid.Html5 doc: https://hackage.haskell.org/package/lucid-2.9.11/docs/Lucid-Html5.html and worked out the function that creates a button:
button_ :: Term arg result => arg -> result
After spending some time trying to work out the correct syntax, I came up with this:
-- correctly replicates the html pasted above
button_ [type_ "button", class_ "btn btn-primary"] "Primary"
Typically I would have called it a victory and focused on other tasks, but this one looks like a true piece of magic to me.
The doc says "button_" is a function that takes an argument "arg" and returns a value of a generic type "result". However, in my application "button_" clearly takes two arguments and returns "Html ()".
-- f arg arg again ??
button_ [type_ "button", class_ "btn btn-primary"] "Primary"
It must do something with the "Term" typeclass, but I'm not sure how to understand it. Can someone help me with this ? I tried loading the module into ghci and inspecting types with ":t", but that didn't help me too much.
The
Term
typeclass is very convenient—we don't need differentterm
functions for creating elements with or without attributes—but can be a bit difficult to understand.The definition of
button_
isterm
is a method of theTerm
typeclass, and has type:That is: you give it the name of the element, some argument whose type depends on the particular instance, and it returns some result whose type depends on the particular instance. But what instances are available? There are three:
This one is for creating attributes, not elements.
This one is for creating elements without attributes. The
arg
we pass as argument toterm
is some piece of html representing the children, and we get another piece of html in return.This one is the most confusing, and the one that is being used in your code. Here,
arg
is a list ofAttribute
values. That much is clear. Butresult
is the type of a function! After passing the attributes, we are left with another functionHtmlT m a
->HtmlT m a
which allows us to supply the contents of the button ("Primary" in your case).The
f ~ HtmlT m a
is another wrinkle that isn't really relevant for this answer. It simply says thatf
is equal toHtmlT m a
. Why not put it directly then? Well, in certain cases it can help drive type inference in desirable ways.