I am not discussing external DSLs in this discussion. Although external DSLs have their use I am reluctant to use them too often, Martin Fowler has a number of informative blog entries regarding the uses and dangers of external DSLs.
Instead I want to discuss the ability to write nice internal DSLs in Scala. Scala provides a number of strategies in which a library author can create a library that can be used in a way that makes sense given the problem. GUI programming may make sense with a declarative syntax. Parser coding might be improved with a DSL that closely mirrors formal grammar.
Why is this such an important issue?
- First one there is the up-front productivity gain. If the problem can be solved in a language that makes sense for the problem then developers do not need to make several translations from the problem space to the program space. In addition if the language suits the problem then most likely the language does not contain much of what Martin Fowler terms: Syntactic Noise.
- The next issue to consider is maintainability and "debuggability". Assuming a well designed DSL (a potentially large assumption), the code should closely align with the problem which means anyone familiar with the problem should be able to follow the logic of the program with relative ease. I think it nearly goes without saying that if the logic can be easily understood then it is easier to debug and maintain.
In my mind XSLT is an example of what happens when the language is not well suited to the problem space. Because XML was chosen as the programming language rather than a more flexible language like Scala or Groovy XSLT is far more difficult to use than it should be.
Some of the issues that make XSLT a burden to use:
- XML is horribly verbose making it slow to program with and to be able to tell what are the important parts of the program.
Here is an example that I think illustrates how difficult XSLT can be to follow.
Where does the XSLT start and stop and where is the new XML defined?
Imagine an XSLT that is 10+ documents and try to follow the logic if you were not part of the development team who wrote the original XSLT.
<xml id="xsltExample" style="width: 400px">
<template match="GM03Comprehensive.Comprehensive.MD_FeatureCatalogueDescription">
<che:CHE_MD_FeatureCatalogueDescription>
<xsl:for-each select="complianceCode">
<complianceCode>
<gco:Boolean>
<xsl:value-of select="."/>
</gco:Boolean>
</complianceCode>
</xsl:for-each>
<xsl:for-each select="includedWithDataset">
<includedWithDataset>
<gco:Boolean>
<xsl:value-of select="."/>
</gco:Boolean>
</includedWithDataset>
</xsl:for-each>
<xsl:for-each select="dataModel">
<che:dataModel xsi:type="che:PT_FreeURL_PropertyType">
<xsl:choose>
<xsl:when test="GM03Core.Core.PT_FreeURL">
<xsl:apply-templates mode="language" select="GM03Core.Core.PT_FreeURL"/>
</xsl:when>
<xsl:otherwise>
<URL><xsl:value-of select="."/></URL>
</xsl:otherwise>
</xsl:choose>
</che:dataModel>
</xsl:for-each>
<xsl:apply-templates mode="Content" select="modelType"/>
</che:CHE_MD_FeatureCatalogueDescription>
</xsl:template>
</xml>
- XSLT is designed to transform XML but unfortunately is written in XML so it is so hard to tell where the declaration starts and the program ends.
- Because it is written in XML you have many restrictions on what characters are permitted and depending if the character is in a text element or an attribute there are different rules.
I am not here to claim that you can't write bad code in Scala. In fact Scala give the programmer a powerful suite of tools which would allow a developer to write the worst code imaginable. Yet for the same reason you can make some of the cleanest and maintainable code out there.
There are those that say allowing developers access to that power is irresponsible because most developers are bad programmers.
Seriously, I have heard this. Even were this true, which I do not believe, a project manager can and should monitor the design of APIs to ensure that all developers have good examples to work from. Also, well-defined best practices can be a big help to reduce the amount of Franken-code.
But I digress.
In contrast to XSLT Scala poses few restrictions on how a library can be declared so that it is possible to create a library that uses a syntax that makes sense for the domain. A couple of examples are in order.
Scala Swing
I have recently been looking at JavaFX and have really enjoyed using it to write Rich UIs (hopefully Oracle doesn't kill it :( )). Using declarative programming for a UI really makes sense. It is easy to read and even if you aren't familiar with the language. Because of Scala's flexibility the Scala team were able to write rich wrappers around Java's Swing library that is nearly as easy to program and read as JavaFX.
Here's a little snippet to whet your appetite (from Scala-swing examples):
object SwingApp extends SimpleGUIApplication {
def top = new MainFrame {
title = "SwingApp"
var numclicks = 0
object label extends Label {
val prefix = "Number of button clicks: "
text = prefix + "0 "
listenTo(button)
reactions += {
case ButtonClicked(button) => {
numclicks = numclicks + 1
text = prefix + numclicks
}
}
}
object button extends Button {
text = "I am a button"
}
contents = new FlowPanel {
contents.append(button, label)
border = Swing.EmptyBorder(5, 5, 5, 5)
}
}
}
The example defines an application with a label that increments each time the button is pressed. This is clearly a different way to do swing programming from the traditional Java swing programming.
Scala XML
Another good example is Scala's XML support. I am not talking about the literals that are embedded into the language (although that makes XML handling much nicer.) I am talking about the library that could be written for any data format; JSON is an example that comes to mind.
val countries = for( country <- XML.load(inputStream) \\ "countries" ) yield {
Country(country \ "@name", country \ "@population", deserializeCities(country \ "cities"))
}
// do something with my scala country objects
This simple example illustrates how the countries elements in an XML document can be deserialized very easily and readably with the Scala XML API. It currently does not have all the power of XSLT but it is getting more and more complete as time passes and is, in my mind, much more readable than XSLT for many common uses.
Actors
One of the libraries that really attracted me to Scala was the Actor's library. Actors were inspired by Erlang Actors and are essentially an implementation of the Active Object pattern (http://www.cs.wustl.edu/~schmidt/PDF/Act-Obj.pdf).
The cool thing about the Actor library in Scala is even though the Actor support is only a library, using it feels like actors are a fundamental part of the language.
val newGuy = actor {
receive {
case msg => {
println("how nice, I just met a person: "+msg);
sender ! "Nice to meet you"
}
}
}
newGuy ! "Hi new guy."
receive { case msg => println("Got a reponse from the new guy: "+msg) }
An obviously silly example but look at how easy it is to construct a new thread and interact with it in a thread-safe manner. It is very clean and very easy to understand the intent of the code. An introduction to actors in scala can be found at: http://www.scala-lang.org/node/242
Other examples:
http://scala.sygneca.com/libs/dbc - A library for accessing Databases
http://code.google.com/p/specs/ - A declarative approach to unit testing
http://www.artima.com/forums/flat.jsp?forum=270&thread=231515 - a discussion on writing internal DSLs in scala
5 comments:
Additionaly you get a benefit over dynamic typed languages: full types information which means support for reliable refactorings and nice content assist
Also I think that very strong internal DSL support in combination with a powerful language gives library designer means to "protect library consumers" by allowing to create safe API-s.
Your XSLT is rather poor. A good rule of thumb is that a template with xsl:for-each and no xsl:apply-templates is most likely wrong.
I took some time to rewrite your example.
XSLT is much easier to read and edit if you use XPath pattern matching instead of treating the language like an ugly version of Pascal.
You can't go wrong with the XSLT reference...
Another interesting use for Scala and XSLT is to use Scala to generate complicated XSLTs as a kind of "low level write only language". :-) You can use the internal XML mode of Scala to write things like
def value(name: String, xpath: String) =
<tr><td>{name}</td><td><xsl:value-of select={xpath}/></td></tr>
and then write down the actual logic of the XSLT sheet in readable Scala as a kind of internal domain specific language.
That example not a good representative of XSLT - it's not even valid XSLT.
Scala does let you approximate XSLT with the xml.transform package [1], but I daresay the equivalent Scala would look at least as verbose and ugly.
The benefits of Scala for that sort
of thing are that Scala is a statically-typed compiled language, and the full language has a lot more expressive power than XSLT; you get to take advantage of the wealth of existing libraries in your processing.
[1] http://www.scala-lang.org/api/current/scala/xml/transform/package.html
Post a Comment