June 2007
[ bamboo ] 11:19, Thursday, 21 June 2007

I had the privilege to attend to Lessig's talk at the FISL some time ago. It really moved me.

I wish him the best of luck on his new crusade.

[ bamboo ] 00:07, Thursday, 21 June 2007

By the way named arguments can also be used with meta methods:

    a = dict(A: "foo", B: "bar")
Possible implementation:
    [meta]
    def dict(keywords as (ExpressionPair)):
        h = [| {} |]
        for pair in keywords:
            h.Items.Add([| $((pair.First as ReferenceExpression).Name): $(pair.Second) |])
        return h
Keyword arguments are all collected into an array passed as the first argument to the meta method. That way, meta methods can still do pattern matching on the number and types of expression arguments.
[ bamboo ] 19:19, Wednesday, 20 June 2007

Boo meta methods are methods that take code trees as input and return code trees as output. In addition they must be marked with the MetaAttribute for the compiler to recognize them as so.

The compiler invokes meta methods during the type resolution phase and replaces the code tree at the point of invocation with the code tree returned by the meta method.

Multiple overloads can be specified in which case the types of the code tree arguments will be used for the purpose of overload resolution.

An example, assert

Assert can be invoked with one or two arguments:

    assert x is null // use the code as the assertion message
    assert x is not null, "x shouldn't be null" // custom exception or string

One way of implementing assert would be:

    [meta] def assert(condition as Expression):
        return [|
            if not $condition:
                raise AssertionFailedException($(condition.ToCodeString()))
        |]
    
    [meta] def assert(condition as Expression, exception as Expression):
        return [|
            if not $condition:
                raise $exception
        |]
Where [| |] are the quasi-quotation delimiters. A quasi-quote evaluates its body as a code tree expression.

$ is generally called the "splice" operator and it means "evaluate me at compilation time".

Alternatively, assert could declare a variable parameter list:

    [meta] def assert(*arguments as (Expression)):
        condition = arguments[0]
        if len(arguments) > 1:
            // assuming 2 arguments
            exception = arguments[1]
        else:
            exception = [| AssertionFailedException($(condition.ToCodeString())) |]
        return [|
            if not $condition:
                raise $exception
        |] 

An interesting aspect of boo's splicing semantics is exemplified by the subexpression:

    [| AssertionFailedException($(condition.ToCodeString())) |]
The splice application $(condition.ToCodeString()) automatically lifts the string value returned by ToCodeString to a proper StringLiteralExpression.

Another example, using

'using' provides for deterministic disposal of resources. The argument should implement the IDisposable interface to have its Dispose method called
at the end of a provided code block.

For instance, the following code:

    using socket=OpenConnection():
        socket.Send("ping")
should be expanded to something equivalent to:
    socket=OpenConnection()
    try:
        socket.Send("ping")
    ensure:
        if socket isa IDisposable: (socket as IDisposable).Dispose()
A possible implementation for 'using' follows:
    [meta] def using(e as Expression, block as BlockExpression):
        temp = uniqueName()
        return [|
            $temp = $e
            try:
                $(block.Body)
            ensure:
                if $temp isa IDisposable: ($temp as IDisposable).Dispose()
        |]

More about quasi-quotation

Due to the reuse of syntactic elements in different contexts the parser needs to follow some conventions in order to infer the meaning of a quasi-quote expression.

Take the quasi-quote [| a as string |]. What does it mean? If we were to interpret it as an expression it would mean a try cast expression. If we were to interpret it as a type member it would mean a field definition.

For the inline form the convention is to try to interpret it as either an expression or an expression pair or an import declaration or a namespace declaration.

The block form is first probed for a type member definition then for a single statement, then for a block of statements.and then for a module.

Example:

    e = [| a as string |]
assert e isa TryCastExpression

f = [|
a as string
|]
assert f isa Field

m = [|
namespace Spam

print "Spam! "*3
|]
assert m isa Module

It should be possible to specify the exact context of quasi-quotation by using some special syntax but the specific details are not clear at this point.

Soon to reach a svn repository near you.

Oh, yeah, and many thanks to the people behind Template Haskell!

[ bamboo ] 12:56, Tuesday, 12 June 2007

Boo 0.7.8 is here!

With many thanks to the people who contributed for this release: Andrew Davey, Avishay Lavie, Cedric Vivier, Chris Prinos, Doug Holton, Jim Lewis and Max Bolingbroke.

What? - http://boo.codehaus.org/
Download - http://boo.codehaus.org/Download
Official irc channel - irc://irc.codehaus.org/boo

Highlights for this release include dramatic improvements to dynamic dispatching performance, a friendlier DSL syntax and of course bug fixes. This is also the last release to support .net 1.1.

Full change log here.

[ bamboo ] 10:20, Thursday, 7 June 2007

My good friend JB has just let me know it was his idea.

Sorry, JB :)

[ bamboo ] 01:01, Thursday, 7 June 2007

Georges showed me today how he was using a boo DSL to generate HTML:

   1:html() do:
   2:    body() do:
   3:        text("Hello, world!")

The idea is to use nested closures to represent the hierarchy of tags:

   1:        
   2:callable Block()
   3:
   4:def blockTag(tagName as string, block as Block):
   5:    print "<${tagName}>"
   6:    block()
   7:    print "</${tagName}>"
   8:    
   9:def html(block as Block):
  10:    blockTag("html", block)
  11:    
  12:def body(block as Block):
  13:    blockTag("body", block)
  14:
  15:def text(s as string):
  16:    print s

It works pretty well except that all those do keywords and parenthesis kind of get in the way of clarity for that particular use case.

What we really would like to write is something like:

   1:html:
   2:    body:
   3:        text "Hello, world!"

Which is clearer and more to the point.

Boo actually allows one to do such a thing by extending the language with macros.

Boo macros are objects that are invoked by the compiler during the compilation process to expand or transform the AST in a hopefully useful way.

Whenever the Boo parser finds code in the form: name expression_list (block)? it creates a MacroStatement node that will be later handled to a macro object for expansion.

Let's define our html, body and text macros for our DSL:

   1:import Boo.Lang.Compiler
   2:import Boo.Lang.Compiler.Ast
   3:
   4:class AbstractTagMacro(AbstractAstMacro):
   5:"""
   6:Maps a macro to a DSL method invocation.
   7:"""
   8:    override def Expand(node as MacroStatement):
   9:        invocation = MethodInvocationExpression(Target: ReferenceExpression(TagName))
  10:        invocation.Arguments.Add(CallableBlockExpression(Body: node.Block)) 
  11:        return ExpressionStatement(invocation)
  12:        
  13:    abstract TagName as string:
  14:        get:
  15:            pass
  16:
  17:class HtmlMacro(AbstractTagMacro):
  18:    override TagName:
  19:        get: return "html"
  20:        
  21:class BodyMacro(AbstractTagMacro):
  22:    override TagName:
  23:        get: return "body"
  24:        
  25:class TextMacro(AbstractAstMacro):
  26:    override def Expand(node as MacroStatement):
  27:        invocation = MethodInvocationExpression(Target: ReferenceExpression("text"))
  28:        invocation.Arguments = node.Arguments
  29:        return ExpressionStatement(invocation)

Now we can write code as we wanted to in the first place but there are a few issues with macros:

  • writing macros demand a in depth knowledge of the compiler object model (oh, you didn't notice it?)
  • macros cannot be used in the same assembly defining them (the compiler needs to instantiate them after all, right?)

Enter BOO-835. It's now possible to use regular method definitions to create DSLs:

   1:callable Block()
   2:
   3:def blockTag(tagName as string, block as Block):
   4:    print "<${tagName}>"
   5:    block()
   6:    print "</${tagName}>"
   7:    
   8:def html(block as Block):
   9:    blockTag "html", block
  10:    
  11:def body(block as Block):
  12:    blockTag "body", block
  13:    
  14:def text(s as string):
  15:    print s
  16:    
  17:html:
  18:    body:
  19:        text "Hello, world!"

The code landed in the repository just a few minutes ago so feel free to try it while it's hot.

Updated: jira issue code was wrong.

[ bamboo ] 13:24, Tuesday, 5 June 2007

I am impressed.

[ bamboo ] 11:48, Sunday, 3 June 2007

Oren asks:

   "Assuming that you have no access to tooling, and you don't have the resources to built NHibernate-sque framework, how would you approach building a Domain Driven application on the naked CLR?"

The most interesting part of the question for me is "and you don't have the resources to built NHibernate" because it immediately goes to the seemingly basic assumption most people have these days that "Persistence => SQL".

While it might be certainly true that a relational backend is a given for most enterprisey scenarios it is certainly not true that all persistent applications have to go through the pain.

Once upon a time a team with 4 people (2 developers, 2 web designers) built the web content management system for the 3rd largest TV station in Brazil on top of System.Runtime.Serialization using the Object Prevalence architecture.

Yeah, skin naked CLR.

If your specific application can't afford keeping all its objects in memory all the time and you don't mind putting a little clothes on, there's db4o.