Update

lucid-svg will not change. The new API will be released as svg-builder.

tl;dr

lucid-svg 0.7 will a breaking change that no longer depends on lucid. The code is on github but I’m holding off on putting it on hackage to give people some time to make suggestions.

Diverging from lucid

lucid-svg was created to address the short comings of blaze-svg in the same spirit as lucid was for blaze-html. After using lucid-svg for about a year now, it’s time to break free of Chris Done’s excellent lucid package. Some of the reasons that I am doing this is that SVG is different enough from html that using an html dsl as the core of lucid-svg is not the best way to go. The remaining justification is simply my personal taste. I want to say straight away, that this decision is in no way a knock of the lucid package, which I think is the best dsl for writing html that is available, in fact I use many of the key ideas from lucid in the lucid-svg 0.7.

Attributes are no longer created like elements.

In previous versions we set svg elements and attributes the same way: function application. For example if we wanted to create a width attribute we might write width_ "100%". The code to create a red rectangle would look something like this,

rect_ [width_ "100%", height_ "100%", fill_ "red"]

I prefer not to conflate elements and attributes by using the same syntax, so in lucid-svg 0.7 we set attributes using the “attribute assignment operator” like Width_ <<- "100%". This operator is called bindAttr and can also be used flipped, "100% ->> Width_". So our previous example becomes:

rect_ [Width_ <<- "100%", Height_ <<- "100%", Fill_ <<- "red"]

The attributes are represented by value constructors ending in an underscore to minimize namespace collisions. This is certainly a bit more verbose, but I think worth it to distinguish between attributes and elements. Attributes can still be composed with the with function.

No more monad instance

The monad instance in lucid, while a decided improvement over the one in blaze is overkill for my purposes. In lucid-svg it was only used to be able to replace monoidal append with do notation. I personally don’t find using <> or mconcat a burden. It always bothered me that the monad in lucid is typically used with a value of type unit, HtmlT m () or in lucid-svg SvgT m (), so we are always throwing away the value and just keeping the side effect. Hence, code that was previously written like this:

contents :: Svg ()
contents = do
  rect_   [ width_ "100%", height_ "100%", fill_ "red"]
  circle_ [ cx_ "150", cy_ "100", r_ "80", fill_ "green"]
  text_   [ x_ "150", y_ "125", fontSize_ "60"
          , textAnchor_ "middle", fill_ "white"] "SVG"

is now written like this:

contents :: Element
contents =
     rect_   [ Width_ <<- "100%", Height_ <<- "100%", Fill_ <<- "red"]
  <> circle_ [ Cx_ <<- "150", Cy_ <<- "100", R_ <<- "80", Fill_ <<- "green"]
  <> text_   [ X_ <<- "150", Y_ <<- "125", FontSize_ <<- "60"
             , TextAnchor_ <<- "middle", Fill_ <<- "white" ] "SVG"

If you really want to use do notation, you could always use the RebindableSyntax language extension with (>>) = (<>).

Less polymorphism

Lucid uses a very overloaded type class so that functions can be used to create both attributes and elements depending on the arguments it is passed. And also to optionally supply a list of attributes as an argument and optionally pass a a child element. We don’t use function application any longer for creating attributes, so we don’t need this case. But more importantly, in SVG we are most often using attributes when creating elements and I’d rather be explicit and pass the empty list when there are no attributes. I did find passing mempty to indicate no child element annoyoing, so we keep the polymorphism here. That is if there are no child elements we can simply leave out the last argument to the function call as we do when using rect_ and circle_ above, otherwise we include them like with text_ in the example. So now to create an element with no attributes we do,

textElement = text_ [] "Hello lucid-svg 0.7"

Other small changes

Since doctype_ is not a function just an SVG element, it has been renamed doctype. The base type for an SVG element is now Element, not Svg ().