Friday, March 5, 2010

Is Go suitable for building DSL?

Computer problems can be approached easier once they are expressed in an effective way. There are modern and general-purpose languages like Ruby and Python that are suitable for building a sort of internal language that is specific for the problem domain: a Domain Specific Language (DSL). A DSL tries to describe the problem with expressivity. But of course, the mean of expressivity is relative to the problem domain. In this blog post I'll explore Go's capability in building DSL.

Rake, a popular Ruby DSL


Rake, like make or ant, is a DSL for defining tasks.

Tasks definitions above are written using Rake DSL. In particular, they define two dependent tasks. They state that the :world task is dependent from the :hello task. In other words, when the :world task is executed, the :hello task will be executed first. This will result in the string "Hello World!" printed on the console. The neat fact about the code above is that it is perfectly legal ruby code. In particular,
  • task is a ruby method that accepts an hash argument
  • :hello and :world are ruby symbols
  • :hello => :world is a ruby hash
  • the do ... end part is a ruby block
Since rake DSL uses valid ruby code to express itself we name it an internal DSL.

Makengo: an internal DSL in Go


When I approached to Go the first time I was intrigued by what is stated in its homepage:

"It feels like a dynamic language but has the speed and safety of a static language. It's a joy to use."

Belonging from years of experience with dynamic languages such as ruby and enjoying its capability to build DSL, I was curious to see how much the compiled and strongly typed nature of Go can be bended in this sense. So, I started writing a small experimental project named makengo.

Using makengo DSL, the previous tasks definitions become:

How does it look compared to the ruby version? First of all, the Go version takes 117 chars against the 75 chars of the Ruby version. So, it's ~56% more verbose. In particular
  • There are too many parentheses
  • String names are longer than symbol names (:hello vs "hello", :world vs "world")
  • The block syntax is burdened by the func keyword
  • In order to express the dependency we need the long named DependsOn function rather than the concise key => value hash syntax
I could shortening the DependsOn() function name and gain some chars but what about expressivity? Rake DSL use Ruby's hash syntax to express dependency but I can't find nothing so effective in Go.

Go DSL capabilities


The retrospection on Makengo raises the main question of this post: is Go suitable for building internal DSLs? Let's try answering, exploring Go's DSL capability in more detail.

Closures


Go has many features that help building DSL. The first thing that comes to mind are anonymous functions that act as closures. In makengo, I use anonymous an function to define what a task does.

Also, anonymous functions can take arguments in order to push values in the block. For example:


Compared with C, this a great step towards expressivity. However, compared with Ruby, Go's anonymous functions syntax appears to be a lot more verbose. This is due to the necessity of a func keyword and to the use of many parentheses. Also, considering that since Go is a strongly typed language, anonymous function arguments need their own type declaration.

Method chaining


In Go, types can receive methods. In Makengo, this fact is exploited to express dependencies among tasks. Actually, the Task function returns a task object and task object can receive the DependsOn method allowing for method chaining:



Dynamic reception and metaprogramming


Go lacks of a ruby-like method_missing method. In ruby, when an unknown method is sent to an object the object responds executing method_missing. This language feature is known as Dynamic Reception. Dynamic Reception, combined with the metaprogramming magic of ruby, allows for very readable DSL (taken from Machinist):

Regarding metaprogramming, Go has an interesting reflect package that allows the manipulation of objects with arbitrary types. This comes in handy, for example, in variadic functions (see below).

Literal maps


In Ruby, a literal map could be passed as argument to a function allowing for more expressive function calls inside the DSL:

Moreover, Ruby allows you omit the delimiters for a literal map. So you can shorten it to:

In Go, a literal map looks like this:

but there is no way to shorten it.

Literal lists and varargs


Like maps, literal lists in Go have not optional delimiters so it's not convenient to use them as function arguments inside DSL. BTW, Go has good support for varargs that combined with types reflection, types assertion and interfaces, are good substitutes for literal lists. For example, in Makengo I can define tasks that depend on more than a single task passing multiple arguments to DependsOn:


A comparison table


Let's try summarize in a table what we discussed until this point.
















































DSL-related featuresGoRuby
Method chainingYesYes
Optional delimiters in mapsNoYes
Optional delimiters in listsNo but you can use varargsYes
MetaprogrammingLimitedYes
Dynamic receptionNoYes
Short signature for blocksNoYes


The answer


So what about the answer to the original question? Is Go suitable for building internal DSL? The answer of course is ... it depends!

As a compiled language, Go can't compete with dynamic languages in building complex and general purpose DSL. However, it's enough flexible to build simple DSL. Moreover, as said at the beginning of this post, the mean of expressivity is relative to the problem domain. Go can be very expressive when dealing with problems that involve network and concurrency issues.

Another road to explore is the implementation of a light VM in Go exploiting some of its advantages against C: built-in concurrency and GC. On top of this VM could live a set of dynamic languages more suited to implement external DSLs. It seems that Eleanor McHugh is on the right track with her GoLightly VM.

Go DSL capability need more investigation, of course. But a fact is clear to me: I should not think at Go as a Panacea trying to force its nature: Go it's not a scripting language. I should rather taking advantage of its peculiarities in order to fully exploit all of its expressivity.

Sunday, January 31, 2010

Gospecify: Basic setup of the project's structure

During the last two days I played a bit with GoSpecify by Samuel Tesla, a BDD framework for the Go Language. I'm coming from 4 years of intense Ruby development and, you know, I could not live without a good BDD framework. After watching Samuel Google TechTalk I decided to explore BDD under Go. The first problem I faced and solved was with basic files and folders setup. In this post I'll describe how I managed the issue using project structure and makefiles taken from gospecify project itself.

First of all, let's create the project folder
mkdir fib
Yeah, you guessed right, we'll deal with yet another Fibonacci sequence implementation ;)

Now, move in fib folder and create a basic project structure
cd fib
mkdir src
mkdir spec
As you guess src will contain source files and spec will contain the specs. Now let's go creating a Makefile in the project base path. Create an empty Makefile using your favourite text editor and copy/paste the code below:
These are the up-level's rules with which we will interact. Names are self-explanatory however I'll describe the corresponding actions below:
make clean
Clean up project folder removing intermediate and backup files

make test
Run the specs

make format
Format the source files using gofmt

make package
Build the package
As you can see, some of the rules above assume there is another Makefile inside src/ folder. Thus, move on src/ and create the following Makefile:
Note that we have to set the PACKAGE variable with the name of the package (fib in this case). Also, note that the testpackage rule in the Makefile above will create a package named test$(PACKAGE).a. So, in our case, we will get src/testfib.a. We will import this package in our test code to actually run the specs.

At this point, the basic project structure and makefiles are done. Now, we can populate src/ folder with an empty source file (with just the basic package clause within):
cd src
echo "package fib" > fib.go
Now, following BDD convention (test-first approach), let's write our test. Enter in spec/ folder and create spec/fib_spec.go with the code below:
The spec above tell us how we expect the system behaves before actually implement it. In particular, we setup two seed values N0 and N1 and then we query fib about the next value in the sequence using fib.Next() method. For the first five calls we expect the sequence: 1, 1, 2, 3, 5.

If we try to run the specs at this point we get an error of course (we haven't an implementation yet):
$ make
...
fib_spec.go:9: undefined: testfib.Fib
...
Open the text editor and paste in src/fib.go the code below:
If you're new to Go, take your time reading the code. Also consider that I'm still learning this language from the ground up so this could not be the best idiomatic implementation (waiting for suggestions in this case). BTW, for the purpose of this post, our code behave as we expect. In fact, running the specs
$ make test
.
Passing: 1 Failing: 0 Pending: 0 Errors: 0
they all pass.

Well, that's all for now! You can browse the source files used in this post on github.

Tuesday, January 26, 2010

Installing Go: go simple!

So finally I decided to run a blog. And yes, you're reading my first blog post ever!

This afternoon I decided to give a whirl to Go Programming Language. I don't know yet so much about it but I'm intrigued by some concepts like goroutines and efficient GC. Moreover, I was amazed by the very first impact with the language: the building process is foolproof (at least on my Ubuntu box) so I decided to document the process as a tribute to this neatness. It's an example of how things should be done in computer programming. Moreover, it's a good starting point for my first blog post ever, isn't it?

Assuming you're on a debian based system you need - first of all - to install the toolchain to building the language:

sudo apt-get install bison gcc libc6-dev ed gawk make

And, if you decide to clone the mercurial repository, you need mercurial of course!

sudo apt-get install mercurial

Now that the prerequisites are satisfied let's configure our build. Here's the neatness of the whole process. You setup a bunch of environment variables and you're done! So for example, assuming that we wish to:
  • Clone Go repository in $HOME/go

  • Build Go for a linux distribution

  • Build Go for a i386 architecture
all we need is to export the following variables:
export GOROOT=$HOME/go
export GOARCH=386
export GOOS=linux
And we can save them in .bashrc if we want a persistent configuration.

The installation process assumes that $HOME/bin folder exists and it places binary files in it. Let's meet this last prerequisite:
mkdir $HOME/bin
You can change binaries' folder setting the $GOBIN variable. In order to run the executables, don't forget to update your $PATH variable appending $HOME/bin.
export PATH=$PATH:$HOME/bin
Well, now let's go cloning the repo and building the whole stuff:
hg clone -r release https://go.googlecode.com/hg/ $GOROOT
cd go/src
./all.bash
At this point the installation process assumes that $HOME/bin folder exists and it places binary files in it (you can change folder name setting the $GOBIN variable). And finally let's test our build starting the compiler:
$ 8g
flags:
-I DIR search for packages in DIR
-d print declarations
-e no limit on number of errors printed
-f print stack frame structure
-h panic on an error
-o file specify output file
-S print the assembly language
-w print the parse tree after typing
-x print lex tokens
And that's all, no autogen.sh, no configure scripts to run! And is even more beautiful because we are actually cross-compiling! That is, we could produce builds for any other supported OS/ARCHs by simply changing the values of the environment variables above! Can't wait to test on my shining new beagleboard when it will arrive! :D

For further readings see: