0

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.

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
Zyphicx
  • 299
  • 2
  • 13
  • 1
    Did you mean `div_`? – melpomene Jun 04 '19 at 15:42
  • Well, this is embarrassing. I looked at it on and off probably for an hour or so and somehow I never noticed that. The error was not quite what I would have expected for a typo. Blunder on my part, thank you very much. – Zyphicx Jun 04 '19 at 15:49
  • So it was the issue? Neat. I'd never heard of Lucid before. – melpomene Jun 04 '19 at 15:50
  • Yes, that was the issue. Lucid uses underscores to avoid conflicts with prelude functions such as div and id. I probably forgot the underscore on a few other occasions, but the compiler warned me since for example nav without an underscore would not be an actual function, but since div is a function I did not get such an error. – Zyphicx Jun 04 '19 at 16:03

1 Answers1

0

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.

melpomene
  • 84,125
  • 8
  • 85
  • 148