On Thu, Nov 13, 2014 at 2:18 AM, Wojtek Narczyński <wojtek@power.com.pl> wrote:
On 13.11.2014 02:07, Christopher Reichert wrote:

I'm not sure if it fits all your criteria but BlazeHtml might interest you.

https://hackage.haskell.org/package/blaze-html


AFAIR, the objective of blaze is speed, rather than correctness of result, so you can do this, for example.

example :: Html
example = do
  H.title $ do
    H.html "rather"
    H.body "strange"
  H.head $ do
    H.h1 "document"

Blaze would be great as a high performance "assembler" for such a DSL.

My question is how to capture (at least some of) the HTML validity rules in Haskell types, or classes, or families, or whatever.


That's pretty much it - or at least how I would tackle it. Start with the blaze structure of a function that takes content and produces a tag. That helps some, as it means you HTML will be properly nested. You now want types for the various types of contents: the "head" and "body" functions return an "HTML" type. "title", "meta" and others return a "HEAD" type. There are at least two types for body elements - block and inline tags. Some tags can be in multiple contexts, so you'll need polymorphic functions, which is where type classes come in.

Attributes represent a different problem. They are basically lists of name/value pairs, but the set of names depends on the tag, and the valid values may as well. You can model them with product types, but that may well  require a type for each tag, which would get repetitive. Some of the values can be checked, others are arbitrary strings.

You might want to take a look at Graphics.OpenSCAD, which tackled a similar problem: providing type checking for 2 and 3d models that were then going to turn into CAD primitives. It has all the same elements - primitives of different types, combinations and operations of those types,some of which that to work on both types, and attributes for all of the above - but for a much less complex domain. Most notable is that not all the checking can be done at compile time. Some of it - like checking the orientation of the faces in a  polyhedron - was much easier to do at generation time.