Domain Specific Languages

I found out Friday night that chromatic from the Perl community has been writing about my previous posts:

Cosine, if you post yet another followup, please explain precisely what in the world you possibly mean by “better support DSLs” and perhaps even “DSL” in general.

Excellent comment, so I will. It goes somewhat to the Lisp idea that code is data, and that data are code. When everything has parentheses around it, that concept is slightly more obvious because they look identical. Here is some code:

(car mylist)

And here are some data:

'(car truck van)

So in Lisp if your list contains symbols that map to function and variable names then you can do an eval and execute your data as code. So one nice thing to do in any language that has an eval function, Perl and Ruby included, is to sometimes have “data” that are run through eval and executed to provide configuration or additional functionality:

do_stuff {
    print "hello world\n";
}

If the right context is created, the above code executes in that context (this example works for both Ruby and Perl due to their similarities), and it provides a powerful way to write small programs with extremely powerful configuration options because the whole force of the underlying programming language is available. The resulting “configuration file language” we create in this process is the DSL.

Let us see what the “hello world” example of a DSL looks like. We will use the above example as our DSL. This DSL defines a do_stuff function that will save the block of code to be executed later. This example will execute the code in the above file just once.

In Ruby (download):

class DoStuff
  def initialize
    instance_eval File.read('config')
  end

  def do_stuff (&block)
    @do_stuff = block
  end

  def run_func
    @do_stuff.call
  end
end

a = DoStuff.new
a.run_func

In Perl (download):

package DoStuff;

our $current_self = undef;

sub new {
  my ($class) = @_;
  my $self = $current_self = {};
  bless $self, $class;
  open CONFIG, "<config";
  my @config = <CONFIG>;
  close CONFIG;
  eval join('', @config);
  $current_self = undef;
  return $self;
}

sub do_stuff (&) {
  my ($func) = @_;
  $current_self->{'do_stuff'} = $func;
}

sub run_func {
  my ($self) = @_;
  &{$self->{'do_stuff'}};
}

package main;

my $a = DoStuff->new();
$a->run_func();

The reason I feel that Ruby supports this better than Perl is twofold:

  1. Ruby has contextual eval statements, whereas Perl has just one eval statement that loses context (this can be seen by use of $current_self above to work around the problem); and
  2. Ruby’s syntax allows for cleaner and more readable DSLs.

Addressing #2 above, if you want a function in your DSL to take arguments and a block of code, then in Ruby you have:

do_stuff(1, 2, 3) { ... #this code might be long }

In Perl the arguments go at the tail of the code (unless you make the code reference explicit):

do_stuff { ... #this code might be long } (1, 2, 3);

If you are reading code in a Perl-based DSL, by the time you get to the end of a block of code and see the arguments for the functions, you may not recall which function it is associated with (unless you bounce back and forth on those curly braces). The arguments are generally used to modify the basic functionality of the function and are easy to comprehend when at the top of the invocation:

do_stuff(:silently, :with => :things, :notify => "matz") { ... }

Anyway, certainly there are Perl enthusiasts out there that will disagree with these opinions. Maybe there is some aspect of Perl I do not understand that makes it easier than I thought in Perl, and if so I would love to know. I still have to use Perl occasionally and so any advancement of that knowledge is useful, too. :) And actually, even though I still find it much easier to write DSLs in Ruby than in Perl, in writing this post’s examples I discovered that writing a DSL in Perl was a lot easier than I thought it was.

  • You can skip to the end and leave a comments. Trackback is currently closed.
  • Trackback URI: http://cosine.org/2007/08/05/domain-specific-languages/trackback/
  • Comments RSS 2.0

4 Responses to “Domain Specific Languages”

  1. hashbangperl Says:

    Cosine,

    Please to be reading TFM ;)

    Hint : perldoc -f do.

    Aristotle ( http://use.perl.org/~Aristotle/journal/34004)couldn’t be bothered to jump through the hoops of registration and logging in just to correct such a silly mistake, but it’s monday morning and the Tax man and a slow paying client have annoyed me already so I’m cranky.

    Also – more importantly than remembering to RTFM, is to not be dynamically eval’ing in the first place – ick! I have to agree with Aristotle on both counts here.

    From the filename, I’d suggest you want to be reading a nice configuration file using one of the nice modules on CPAN. Lately I’ve been using Config::Context, which gives me Apache style per host and directory configuration – incredibly handy for my Perl MVC application so I can deploy a web application once, but with configuration per host and path. Sweet as!

  2. Ovid Says:

    Hi Cosine,

    I think it would be good if you read up on the Wikipedia entry on DSLs (http://en.wikipedia.org/wiki/Domain-specific_programming_language). Since I’m not sure what formatting is allowed here, I wrote up a journal entry on use.perl (http://use.perl.org/~Ovid/journal/34010).

    Cheers,
    Ovid

  3. mst Says:

    Here’s how I’d write it (code not actually tested but should be close enough) -

    package DoStuff;

    use Moose;
    use IO::All;

    has ‘func’ => (is => ‘rw’);

    sub BUILD {
    my $self = shift;
    local *do_stuff = sub (&) { $self->func($_[0]); };
    eval io(‘config’)->slurp;
    }

    sub run_func { shift->func->(); }

    Wonderful thing, modern perl.

  4. PansyHerman23 Says:

    That is perfect that we are able to take the mortgage loans and that opens up new possibilities.

Leave a Reply

You must be logged in to post a comment.