Faking Builders With Gosu

One of the features of Groovy (being aped by Kotlin) is builders, a nice syntactic way to compose nested content.

A canonical example of this, I think, would be building an HTML page. The Groovy page has an example of this:

html {
    head {
        title"XML encoding with Groovy"
    }
    body {
        h1"XML encoding with Groovy"
        p"this format can be used as an alternative markup to XML"

        / an element with attributes and text content /
        ahref:'http://groovy.codehaus.org' ["Groovy"]

        / mixed content /
        p [
            "This is some",
            b"mixed",
            "text. For more see the",
            ahref:'http://groovy.codehaus.org' ["Groovy"],
            "project"
        ]
        p "some text"
    }
}

Gosu does not support builders, but I thought it would be fun to see how close we could get to them with the syntactic tools that Gosu does have. Using names arguments and default parameters, in particular, I think we could get pretty darned close.

So, the first trick I’m going to use in faking builders is something I call the Keefer Gambit: I’m going to create an empty HtmlUtil interface and then create an enhancement of that interface that supplies all the methods you will use to build html. This allows you to mix in the HTML functionality wherever you want it by simply implementing the (empty) interface. This (hack|elegant solution) for mixins in Gosu was first pointed out by Alan Keefer, hence the name.

OK, given that general approach, the syntactic tools we can use to fake builders are named arguments and default parameters. Let’s look at implementing the html element method in our enhancement:

  function html( head : String = "", body : List<String> = null) : String {
    var bodyContent = body == null ? "" : body.join( "\n" )
    return "<html><head>\n${head}</head>\n<body>${bodyContent}</body>\n</html>"
  }

Nothing too fancy here: there are two parameters, each with a default value. The function will return these two elements, wrapped up in the appropriate tags. If the parameters are omitted then the default values are used, and you can use named parameters when invoking this method because it is defined in Gosu.

I’m not going to walk through the implementation of each method, rather I’ll just point you to the sample code I put up on GitHub here:

    https://github.com/carsongross/BuilderExample

But using that code, I can write this code in an Example client:

html(
    :head = head(
        :title = "XML encoding with Gosu"
    ),
    :body = {
        h1( "XML encoding with Gosu" ),
        p( "this format can be used as an alternative markup to XML" ),
        /* an element with attributes and text content */
        ahref( 'http://gosu-lang.org', "Gosu"),
        /* mixed content  */
        p({
            "This is some",
            b("mixed"),
            "text. For more see the",
            ahref('http://gosu-lang.org', "Gosu"),
            "project"
        }),
        p( "some text" )
    }
)

Now, I’ll admit that isn’t quite as clean as the Groovy: we violate the DRY principle when we generate the head element, for example. And things are a bit dicier around when you use parens vs. curly braces. Also, I have to admit, this is a relatively simple example. But I think that this is a pretty clean translation nonetheless, and it was doable without adding any new syntactic features to Gosu.

Gosu, unlike Groovy, Scala or Ruby, requires parentheses around method arguments, and it doesn’t support more exotic things like var-args (it has a nice list literal instead) or implicit arguments: we favor regularity over succinctness beyond a certain point. Despite this, I’m comfortable with the expressiveness of the syntax of Gosu.

I’d rather see innovations in libraries, leveraging The Open Type System rather than much continued innovation in the core language syntax.

I’ll be at OSCON this week. I’m always happy to talk gosu and/or drink beer if you are up here.


4 Comments on “Faking Builders With Gosu”

  1. Yehoram Shenhar says:

    Carson,
    We have been using the ‘Keefer Gambit’ to add general purpose methods to test classes (clean up etc’). I like the fact that it also enable a cleaner fluent API, similar to static imports in Java.

    • Carson Gross says:

      Nice.

      Yeah, it seems like a funky work around for a lot of language problems, and holds up pretty well as long as you don’t try to get too polymorphic with it.

  2. Generally I do not read post on blogs, however I wish to say that this write-up very forced
    me to check out and do it! Your writing taste has been amazed me.
    Thanks, very nice post.

  3. If a good user requires help, the user can make utilization of technical support based upon text-based modules and he or she needs
    to type the issues in the search field. In most cases, the official Hotmail tech support team will certainly be able to protect all
    of your problems but within some cases, this is not enough to
    resolve your issues.


Leave a comment