ANN: nice-html, an all-around "nice" html templating library, similar but different to lucid, blaze, type-of-html, etc.

Hi Haskell-Cafe, I've been working on and off on a HTML templating library that is slightly novel for a few months. Here's the good parts: * it's faster than lucid and blaze * it has a valid monad instance * it has another valid monad instance, that is slightly different! * it paramaterises templates by the data they require * the HTML5 combinators don't rely on any highly overloaded/overlapping typeclasses; type errors in nice-html templates tend to be nice monomorphic type mismatches To achieve this, it has a distinct compilation phase (at runtime) for templates. This means the actual rendering function does very little beyond concatenate Text and escape dynamic input. It also severely restricts usage to the built-in combinators available -- at least, for dynamic data -- and can make writing a nice-html template difficult, though littering _ throughout type signatures helps. Check it out! Benchmark results, a README, and an example: https://github.com/TransportEngineering/nice-html Hackage: https://hackage.haskell.org/package/nice-html The only thing on the roadmap right now is to have some nice :-) way to assign JavaScript event handlers to Markup. This is something that I really appreciate when using React.js, so my eventual aim is for nice-html to be like a (more) server-side version of React. Right now, you can keep track of element ids with `Text.Html.Writer.note`, but it's neither a very good nor very useful interface. Cheers, Mike

Thank you for putting comparisons and benchmarks, upfront.
Do you mind explaining the invalid monad instance of Lucid and how it would
impact a library user?
Also, I'm assuming one shouldn't call the `compile` function each time a
page needs to be rendered, right? Should one put the results of the
`compile` function in a top-level Map and use those to get the speed gains?
Side comment, the use of `dynamic` makes it look similar to reflex `dyn*`
API.
-- Saurabh.
On Thu, Oct 26, 2017 at 4:15 PM, Mike Ledger
Hi Haskell-Cafe,
I've been working on and off on a HTML templating library that is slightly novel for a few months.
Here's the good parts: * it's faster than lucid and blaze * it has a valid monad instance * it has another valid monad instance, that is slightly different! * it paramaterises templates by the data they require * the HTML5 combinators don't rely on any highly overloaded/overlapping typeclasses; type errors in nice-html templates tend to be nice monomorphic type mismatches
To achieve this, it has a distinct compilation phase (at runtime) for templates. This means the actual rendering function does very little beyond concatenate Text and escape dynamic input. It also severely restricts usage to the built-in combinators available -- at least, for dynamic data -- and can make writing a nice-html template difficult, though littering _ throughout type signatures helps.
Check it out!
Benchmark results, a README, and an example: https://github.com/ TransportEngineering/nice-html Hackage: https://hackage.haskell.org/package/nice-html
The only thing on the roadmap right now is to have some nice :-) way to assign JavaScript event handlers to Markup. This is something that I really appreciate when using React.js, so my eventual aim is for nice-html to be like a (more) server-side version of React. Right now, you can keep track of element ids with `Text.Html.Writer.note`, but it's neither a very good nor very useful interface.
Cheers, Mike
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user? To my knowledge lucid uses a monad instance that follow the monad laws (what I meant by "valid") fine.
Also, I'm assuming one shouldn't call the `compile` function each time a
It's blaze that's (somewhat) infamous for having an invalid monad instance.
It used to be the case that (>>=) in blaze called 'error' -- the Monad
instance was to just use (>>) = mappend. Looking now, the implementation
has changed and my knowledge was out-of-date. I think it still violates the
monad laws, but I don't know if as a user of blaze you'd ever be able to
actually observe this, if that makes any sense.
For some fun discussions on this see:
-
https://stackoverflow.com/questions/6399648/what-happens-to-you-if-you-break...
-
https://www.reddit.com/r/haskell/comments/16iakr/what_happens_when_a_monad_v...
How invalid monads *can* impact users is better explored in the SO question.
page needs to be rendered, right? Should one put the results of the
`compile` function in a top-level Map and use those to get the speed gains?
The idea is to only compile a template once, ever. If you need to compile a
template every time a page is rendered (though I can't actually think of a
case where you would), you should probably just skip the middle-man, and
use lucid (or blaze). It's not horribly slow, but you'd miss out on the
benefits of nice-html by doing this.
Cheers
On Thu, Oct 26, 2017 at 11:51 PM, Saurabh Nanda
Thank you for putting comparisons and benchmarks, upfront.
Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user?
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains?
Side comment, the use of `dynamic` makes it look similar to reflex `dyn*` API.
-- Saurabh.
On Thu, Oct 26, 2017 at 4:15 PM, Mike Ledger
wrote: Hi Haskell-Cafe,
I've been working on and off on a HTML templating library that is slightly novel for a few months.
Here's the good parts: * it's faster than lucid and blaze * it has a valid monad instance * it has another valid monad instance, that is slightly different! * it paramaterises templates by the data they require * the HTML5 combinators don't rely on any highly overloaded/overlapping typeclasses; type errors in nice-html templates tend to be nice monomorphic type mismatches
To achieve this, it has a distinct compilation phase (at runtime) for templates. This means the actual rendering function does very little beyond concatenate Text and escape dynamic input. It also severely restricts usage to the built-in combinators available -- at least, for dynamic data -- and can make writing a nice-html template difficult, though littering _ throughout type signatures helps.
Check it out!
Benchmark results, a README, and an example: https://github.com/TransportEngineering/nice-html Hackage: https://hackage.haskell.org/package/nice-html
The only thing on the roadmap right now is to have some nice :-) way to assign JavaScript event handlers to Markup. This is something that I really appreciate when using React.js, so my eventual aim is for nice-html to be like a (more) server-side version of React. Right now, you can keep track of element ids with `Text.Html.Writer.note`, but it's neither a very good nor very useful interface.
Cheers, Mike
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Please, if you think blaze-markup violates the Monad laws, then report a counterexample so it will get fixed. The changelog for 0.8.0.0 [1] advertises: Make `MarkupM` finally adhere to the Monad laws http://hackage.haskell.org/package/blaze-markup-0.8.0.0/changelog Cheers, Oleg. On 26.10.2017 16:54, Mike Ledger wrote:
Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user? To my knowledge lucid uses a monad instance that follow the monad laws (what I meant by "valid") fine.
It's blaze that's (somewhat) infamous for having an invalid monad instance. It used to be the case that (>>=) in blaze called 'error' -- the Monad instance was to just use (>>) = mappend. Looking now, the implementation has changed and my knowledge was out-of-date. I think it still violates the monad laws, but I don't know if as a user of blaze you'd ever be able to actually observe this, if that makes any sense.
For some fun discussions on this see: - https://stackoverflow.com/questions/6399648/what-happens-to-you-if-you-break... - https://www.reddit.com/r/haskell/comments/16iakr/what_happens_when_a_monad_v...
How invalid monads /can/ impact users is better explored in the SO question.
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains? The idea is to only compile a template once, ever. If you need to compile a template every time a page is rendered (though I can't actually think of a case where you would), you should probably just skip the middle-man, and use lucid (or blaze). It's not horribly slow, but you'd miss out on the benefits of nice-html by doing this.
Cheers
On Thu, Oct 26, 2017 at 11:51 PM, Saurabh Nanda
mailto:saurabhnanda@gmail.com> wrote: Thank you for putting comparisons and benchmarks, upfront.
Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user?
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains?
Side comment, the use of `dynamic` makes it look similar to reflex `dyn*` API.
-- Saurabh.
On Thu, Oct 26, 2017 at 4:15 PM, Mike Ledger
mailto:eleventynine@gmail.com> wrote: Hi Haskell-Cafe,
I've been working on and off on a HTML templating library that is slightly novel for a few months.
Here's the good parts: * it's faster than lucid and blaze * it has a valid monad instance * it has another valid monad instance, that is slightly different! * it paramaterises templates by the data they require * the HTML5 combinators don't rely on any highly overloaded/overlapping typeclasses; type errors in nice-html templates tend to be nice monomorphic type mismatches
To achieve this, it has a distinct compilation phase (at runtime) for templates. This means the actual rendering function does very little beyond concatenate Text and escape dynamic input. It also severely restricts usage to the built-in combinators available -- at least, for dynamic data -- and can make writing a nice-html template difficult, though littering _ throughout type signatures helps.
Check it out!
Benchmark results, a README, and an example: https://github.com/TransportEngineering/nice-html https://github.com/TransportEngineering/nice-html Hackage: https://hackage.haskell.org/package/nice-html https://hackage.haskell.org/package/nice-html
The only thing on the roadmap right now is to have some nice :-) way to assign JavaScript event handlers to Markup. This is something that I really appreciate when using React.js, so my eventual aim is for nice-html to be like a (more) server-side version of React. Right now, you can keep track of element ids with `Text.Html.Writer.note`, but it's neither a very good nor very useful interface.
Cheers, Mike
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- http://www.saurabhnanda.com
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

The changelog for 0.8.0.0 [1] advertises: Make `MarkupM` finally adhere to the Monad laws Nice. I searched the issue tracker on GitHub but ought to have just looked at the changelog.
Please, if you think blaze-markup violates the Monad laws, then report a counterexample so it will get fixed. Not really. Hence the caveat. But just to clarify, my thinking* was this:
return a >>= f
= Empty a >>= f
= Append (Empty a) (f (markupValue (Empty a)))
= Append (Empty a) (f a)
≠ f a
*BUT *you can only observe this by importing Blaze.Text.Internal and
pattern matching on MarkupM. Using renderMarkup you can't observe it. So
long as a user can't observe the difference (or is at least discouraged by
a scary-looking .Internal suffix), it's just an implementation detail that
the data constructors don't exactly match.
Cheers,
Mike
*: using = and ≠ to mean "exactly the same as", like a derived Eq instance,
rather than e.g.
https://github.com/jaspervdj/blaze-markup/blob/master/tests/Text/Blaze/Tests...
which compares different Markup renderings.
On Fri, Oct 27, 2017 at 1:53 AM, Oleg Grenrus
Please, if you think blaze-markup violates the Monad laws, then report a counterexample so it will get fixed.
The changelog for 0.8.0.0 [1] advertises: Make `MarkupM` finally adhere to the Monad laws
http://hackage.haskell.org/package/blaze-markup-0.8.0.0/changelog
Cheers, Oleg.
On 26.10.2017 16:54, Mike Ledger wrote:
Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user? To my knowledge lucid uses a monad instance that follow the monad laws (what I meant by "valid") fine.
It's blaze that's (somewhat) infamous for having an invalid monad instance. It used to be the case that (>>=) in blaze called 'error' -- the Monad instance was to just use (>>) = mappend. Looking now, the implementation has changed and my knowledge was out-of-date. I think it still violates the monad laws, but I don't know if as a user of blaze you'd ever be able to actually observe this, if that makes any sense.
For some fun discussions on this see: - https://stackoverflow.com/questions/6399648/what- happens-to-you-if-you-break-the-monad-laws - https://www.reddit.com/r/haskell/comments/16iakr/what_ happens_when_a_monad_violates_monadic_laws/
How invalid monads /can/ impact users is better explored in the SO question.
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains? The idea is to only compile a template once, ever. If you need to compile a template every time a page is rendered (though I can't actually think of a case where you would), you should probably just skip the middle-man, and use lucid (or blaze). It's not horribly slow, but you'd miss out on the benefits of nice-html by doing this.
Cheers
On Thu, Oct 26, 2017 at 11:51 PM, Saurabh Nanda
mailto:saurabhnanda@gmail.com> wrote: Thank you for putting comparisons and benchmarks, upfront.
Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user?
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains?
Side comment, the use of `dynamic` makes it look similar to reflex `dyn*` API.
-- Saurabh.
On Thu, Oct 26, 2017 at 4:15 PM, Mike Ledger
mailto:eleventynine@gmail.com> wrote: Hi Haskell-Cafe,
I've been working on and off on a HTML templating library that is slightly novel for a few months.
Here's the good parts: * it's faster than lucid and blaze * it has a valid monad instance * it has another valid monad instance, that is slightly different! * it paramaterises templates by the data they require * the HTML5 combinators don't rely on any highly overloaded/overlapping typeclasses; type errors in nice-html templates tend to be nice monomorphic type mismatches
To achieve this, it has a distinct compilation phase (at runtime) for templates. This means the actual rendering function does very little beyond concatenate Text and escape dynamic input. It also severely restricts usage to the built-in combinators available -- at least, for dynamic data -- and can make writing a nice-html template difficult, though littering _ throughout type signatures helps.
Check it out!
Benchmark results, a README, and an example: https://github.com/TransportEngineering/nice-html https://github.com/TransportEngineering/nice-html Hackage: https://hackage.haskell.org/package/nice-html https://hackage.haskell.org/package/nice-html
The only thing on the roadmap right now is to have some nice :-) way to assign JavaScript event handlers to Markup. This is something that I really appreciate when using React.js, so my eventual aim is for nice-html to be like a (more) server-side version of React. Right now, you can keep track of element ids with `Text.Html.Writer.note`, but it's neither a very good nor very useful interface.
Cheers, Mike
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- http://www.saurabhnanda.com
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Do you mind explaining the invalid monad instance of Lucid and how it would impact a library user? To my knowledge lucid uses a monad instance that follow the monad laws (what I meant by "valid") fine.
Sorry for causing the confusion. I misread that statement in your README.
Also, I'm assuming one shouldn't call the `compile` function each time a page needs to be rendered, right? Should one put the results of the `compile` function in a top-level Map and use those to get the speed gains? The idea is to only compile a template once, ever. If you need to compile a template every time a page is rendered (though I can't actually think of a case where you would), you should probably just skip the middle-man, and use lucid (or blaze). It's not horribly slow, but you'd miss out on the benefits of nice-html by doing this.
So, I guess my question was, how does one compile a template exactly once with `nice-html` ? If some content inside the template is conditional based on input values, will it work properly, as expected? -- Saurabh.
participants (3)
-
Mike Ledger
-
Oleg Grenrus
-
Saurabh Nanda