Lucid nav element gives error about attribute list

65 Views Asked by At

I am currently in the process of rewriting an HTML file in Lucid for use with a Spock web server. However, for some reason this particular snippet gives me an error:

sidebar :: Html ()
sidebar = do
  nav_ [id_ "sidebar"] $ do
    div [class_ "sidebar-header"] $
      h3_ "Sidebar"

    div [class_ "list-group"] $ do
      a_ [href_ "#", class_ "menuItem list-group-item rounded-0"] "Item 1"
      a_ [href_ "#", class_ "menuItem list-group-item rounded-0"] "Item 2"

Namely, the error is:

Couldn't match type `[Attribute]'
               with `HtmlT Data.Functor.Identity.Identity ()'
  arising from a use of `nav_'

I noticed that removing the div and just having the h3 fixes the problem, but that is not what I want. I did some googling to try to find the issue, but from what I could see the library does not have that many examples online that I could try to look at. Stackoverflow and Reddit searches did not reveal anything either.

This is my first actual project where I have used Lucid, so it is entirely possible that there is an obvious blunder somewhere.

1

There are 1 best solutions below

0
On

The problem is the use of div (integer division) instead of div_ (HTML element).

The type error is slightly bizarre, but arises from how the type checker tries to infer a type for the whole expression.

class_ has type

class_ :: Text -> Attribute

Thus [class_ "list-group"] :: [Attribute]. That's easy.

div has type

div :: Integral a => a -> a -> a

There is an Integral constraint, but more importantly, both input types and the result type must be the same.

In an expression like

div [class_ "list-group"] $ do
  a_ [href_ "#", class_ "menuItem list-group-item rounded-0"] "Item 1"
  a_ [href_ "#", class_ "menuItem list-group-item rounded-0"] "Item 2"

the type checker concludes that [class_ "list-group"], do ..., and the whole div ... ... expression have the same type.

We know the first type, so we conclude do { a_ ...; a_ ... } :: [Attribute] and div [...] $ do ... :: [Attribute].

This use of div is the last statement in a do block, which means its type is also the type of the whole do expression.

I'm a bit fuzzy on the next part, but I think the type checker looks at

nav_ :: Term arg result => arg -> result

and

class Term arg result | result -> arg

and the available Term instances, and concludes that because the declared result type is sidebar :: Html (), the second argument to nav_ (the do block) must also have type Html (), which is an alias for HtmlT Identity ().

Now we have a conflict: The declared type signature says the second argument of nav_ must be a HtmlT Identity (), but the inferred type is [Attribute].

At this point the type checker just gives up and reports the confusing problem to the user.