Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,583
|
Comments: 51,213
Privacy Policy · Terms
filter by tags archive
time to read 1 min | 40 words

I am toying with this DSL:

recieve:
	message msg as ChangeAddressMessage if msg.AddressId is null:
		transaction:
			address = Address.FromMessage(msg)
			address.Create()		
	message msg as ChangeAddressMessage:
		transaction:
			address = LoadAddress(msg.AddressId)
			Address.FromMessage(address, msg)
			address.Save()
	message other:
		raise MessageNotUnderstood()

What book am I reading now?

time to read 5 min | 969 words

Michael Dorfman presents an interesting question about building internal DSLs.

[You should] point out for those of us who have not (yet) tasted the Boo koolaid exactly what makes this language more suitable to DSLs than VB9, C#3, F#, IronPython or IronRuby.
It's not enough to say "You can't build DSLs in C# / VB, they aren't flexible enough (at all)", you need to mention which language features are missing.

So, why am did I say that C#/VB/Java/etc are not flexible enough to build usable DSLs out of?

Let us consider what we want to achieve when we are thinking about a DSL. The first thing that comes to mind is that is should be about the domain, so I want to have as few programming related stuff as possible, and the syntax should be simple and clear to read.

Statically typed languages are historically rigid in their structure, and force you to follow specific syntax rules. Dynamic languages are far more relaxed in that manner. Boo is a statically typed language that masquerade as a dynamic language, so it sits with the dynamic languages on this matter.

I am not familiar enough with F# to give an answer on that, but both ruby & python can certainly build DSLs. DSLs are very common in ruby, and I think that python can do much the same, although I do wonder about the white space sensitivity thing regarding DSLs.

But let us explore for a moment the idea of build a DSL in a statically typed language. We want to define a DSL for executing tasks in a build process. We want to execute a process and copy some files. C# (for java, just change the using to imports):

using My.Imaginary.Build.Framework;
using My.Imaginary.Build.Framework.IO;

public class MyCustomTask : TaskBase
{
	public MyCustomerTask() : TaskBase("Execute process & copy some files")
	{	
	}

	public override void Execute()
	{
		Execute("msbuild.exe", "myProject.sln");
		CopyFiles("bin\debug\**.*", "ftp://staging-srv/myApp/");
	}
}

And in VB.Net:

Imports My.Imaginary.Build.Framework
Imports My.Imaginary.Build.Framework.IO
 
Public Class MyCustomTask Inherits TaskBase

	Public Sub New()
		MyBase.New("Execute process & copy some files")
	End Function
 
	Public Overrides  Sub Execute()
		Execute("msbuild.exe", "myProject.sln")
		CopyFiles("bin\debug\**.*", "ftp://staging-srv/myApp/")
	End Sub
	
End Class

There is a lot of syntactic noise here that we need just to make the compiler happy. We aren't working with a DSL, at best, we are working with a fluent interface (more on that later).

We could probably do this, though:

Description("Execute process & copy some files");
Execute("msbuild.exe", "myProject.sln");
CopyFiles("bin\debug\**.*", "ftp://staging-srv/myApp/");

And then pre-process the file in order to put all the rest of the noise around it, but that is going to be bite us if we need to do things such extending it further (how do we add a using statement here? how do we define a method?), or have anything even slightly more complex than a series of sequential operations?

Sure, I can think of solutions for all of the above, but they #include stuff that is (a) hard, (b) unpleasant, (c) complex and (d) leaky.

When you are building DSLs, you want as few constraints as possible from the language, and that is not possible to do in static languages. But, I can hear you say, we have Linq and extension methods and fluent interfaces now, surely that is a solved problem.

Not really, I believe that I have pushed C# 2.0 as far as it is possible to go in regards to fluent interfaces, and I have looked very closely at what I can do with C# 3.0 new syntax. The answer is that it is great for programmers, but not really useful for developing DSLs. You are still going to have to deal with making the compiler happy, and your ability to modify the syntax of the language itself to fit the ideas that you want to express is nil.

In comparison, here is how you can express the same idea in Boo:

Task "Exexcute process & copy some files:
	Execute "msbuild.exe", "myProject.sln"
	CopyFiles "bin\debug\*.*", "ftp://staging-srv/myApp"	

There is nothing here that is here to make the compiler happy, we are focused on what we want to do, not on the syntax.

Beyond that, we have the Boo compiler itself, which allows you to go far beyond that. This goes a bit beyond the scope of DSL, but this is valid Boo code:

receive:
	message msg as ChangeAddressMessage if msg.AddressId is null:
		transaction:
			address = Address.FromMessage(msg)
			address.Create()		
	message msg as ChangeAddressMessage:
		transaction:
			address = LoadAddress(msg.AddressId)
			Address.FromMessage(address, msg)
			address.Save()
	message other:
		raise MessageNotUnderstood()

Boo doesn't have the receive / message primitives, nor a transaction keyword, but it is trivial to add them (well, trivial to add the keywords, the messaging infrastructure is not trivial).

This expose an interesting concept that I have yet to talk about, Boo allows me to do code transformation at compile time. This means that even if the way that we want to express something is not the way we would like it to execute, we can have the best of both worlds, we can express it clearly, and translate it to the way it should be executed.

That is not something that you can really do in most other languages, certainly not in statically typed ones. Yeah, I know about assembly weaving, that is now what I call a DSL. I think that Ruby can do something similar, using ParseTree and eval(str) {don't think Ruby has a way to translate expression list to code, but again}, but I am not sure.

So, anyway, those are my reasoning for using Boo.

time to read 1 min | 121 words

I know that I am asking the question from a self-selecting group, but what the hell.

I am considering the idea of writing a book about writing domain specific languages, with Boo as the implementation language. This has two distinct topics:

  • Theoretical knowledge:
    • DSL Usages
    • How to create the syntax
    • How to integrate into an application
    • Testing
    • Versioning
    • Patterns
  • Implementation strategy - how to actually build a useful DSL that can do all of the above using Boo.

I certainly like the subject, but I wanted to know what you think about it.

Do you find the idea useful? Is there enough interest in it? Do you (or people you know) use DSLs for work/fun? Any other comment?

time to read 1 min | 127 words

Okay, since I have started thinking about using:

import file from anotherFile.boo

It occurred to me that there are plenty of reasons to want to do similar things. And boo is flexible enough to make some interesting things with it.

What about bringing in WSDL definition at compile time?

import webservice from "http://localhost/who/is.there" as WhoIsThereService

And then writing a small compiler step that does all the rest.

Usually the first twenty or so lines of my my Windsor.boo file are just import statements, they really make it harder to read, why not split them to another file?

import all from imports.boo

I can probably think of a few other interesting things to do with it, as well.

Multi file DSLs

time to read 3 min | 417 words

imageWhile a DSL can really cut down the amount of code that you need to write, putting everything in one file is not a good idea. But I like my DSLs to be script  like, without the need for an explicit compilation step. I have thought about it for a while now, and yesterday ago Craig Neuwirt* asked how it can be done using Binsor.

My default language is Boo, obviously, which is a static, compiled language that pretends that it is dynamic and scriptish, giving the best of all worlds. One of the cool things about Boo is that you can reference an assembly by just doing this:

import System.Data.SqlClient from System.Data

This make it very easy to write scripts, because you just declare things and they will be automatically referenced, without needing to pass parameters to the script runner.

Now, I wanted to have some way to handle file inclusion as well. But Boo doesn't support this. This is not something that you can do in a method, or dynamic loading, you have to do this as part of the compilation process.

In a normal language, I would be left with building pre-processors, or doing heuristics to find all the relevant files that I want, or write an XML file that would detail which file I want to include. But Boo is not a normal language, it is a Super Language. It let me do what I want. And I wanted to have file references.

So I did.

I love Boo, I really do. The initial sample I started with was this. This is actually on the examples, what do you know. Then I thought about how I wanted to it, and I figured out that I can do this:

import file from  anotherFile.boo

Boo allows me to modify the compiler object model (AST) at compilation, so I just detect all the imports for the namespace "file", and then I compile and reference that file. I can even do this:

import file from  "~/config/anotherFile.boo"

Where ~ is translated to the AppDomain BaseDirectory.

You can find the code here, about 100 lines of code, out of which around 30 could be YAGNIed away. Wasn't very hard, and make me love Boo all over again, for what it allows me to do.

* Who doesn't blog, and a search for "Craig Neuwirt blog" brings back my blog :-) (hint)

Redefining If

time to read 1 min | 80 words

I had time today to sit on several Brail bugs. I keep getting more and more and more amazed by the power that Boo is giving me.

In order to fix some of those bugs, I had to literally change the meaning of the if statement. (I defined my own nullable propagator, which I had to implement deeply into the language).

Damn, I love this language.

Oh, and FYI, if 2+2 == 4 will not do the expected thing anymore :-) 

BooBS Sample

time to read 3 min | 540 words

Here is the first such script that I am going to use:

import Boobs.IO.Extensions
import Boobs.Compiler.Extensions

Task
"default", ["build"]

Task
"build", ["clean"]:
      MsBuild("MyApp.sln").Execute()

Task "clean":
      MsBuild("MyApp.sln", "/target:clean").Execute()

Svn "ContiniousIntegration":
      SvnUpdate
      Execute("default")

Note that this is probably incredibly primitive consider what rake can do, but it amuses me to be able to do this.

The commands are probably even more fun:

Tools\BooBuildSystem\boobs -f:default.boobs -t:ContiniousIntegration

The Svn task will only be run if the repository is more recent than the working copy, so this is something that makes it very easy to build CI scripts. In this case I need to do something a distributed deployment on commit, and while I can probably do this using CC.Net, I am not really fond of the anticipated pain (configuring permissions alone would be a PITA, I imagine) and the executable XML.

This is far simpler, if more primitive.

As an aside, I am getting addicted to parenthesis-less method calls. 

time to read 2 min | 332 words

I hate XML, a long time ago, I also hated XML, but I also had some free time, and I played with building a build system in Boo. To match NAnt, I called it NUncle.

It never really gotten anywhere, but Georges Benatti has taken the code and created the Boo Build System. I am just taking a look, and it is fairly impressive. It has the concept of tasks and dependencies between them, as well as action that it can perform.

Here is a part of Boobs' own build script:

Task "build boobs", ["build engine", "build extensions"]:
	bc = Booc(
		SourcesSet   : FileSet("tools/boobs/**/*.boo"),
		OutputFile   : "build/boobs.exe"
		)
	bc.ReferencesSet.Include("build/boobs.engine.dll")
	bc.ReferencesSet.Include("build/boo.lang.useful.dll")
	bc.Execute()

Task "build engine":
	Booc(
		SourcesSet  : FileSet("src/boobs.engine/**/*.boo"),
		OutputFile  : "build/boobs.engine.dll",
		OutputTarget: TargetType.Library 
		).Execute()

Task "build extensions", ["build io.extensions", "build compiler.extensions"]

Task "build io.extensions":
	Booc(
		SourcesSet  : FileSet("src/extensions/boobs.io.extensions/**/*.boo"),
		OutputFile  : "build/boobs.io.extensions.dll",
		OutputTarget: TargetType.Library 
		).Execute()

Task "build compiler.extensions":
	bc = Booc(
		SourcesSet   : FileSet("src/extensions/boobs.compiler.extensions/**/*.boo"),
		OutputFile   : "build/boobs.compiler.extensions.dll",
		OutputTarget : TargetType.Library 
		)
	bc.ReferencesSet.Include("build/boobs.io.extensions.dll")
	bc.Execute()

I don't know about you, but this makes me feel very nice.

The concept is pretty obvious, I feel, and the really nice thing is that extending it is a piece of cake. Here is how you validate dates of two files:

def IsUpToDate(target as string, source as string):
	return true unless File.Exists(source)
	return false unless File.Exists(target)

	targetInfo = FileInfo(target)
	sourceInfo = FileInfo(source)
		
	return targetInfo.LastAccessTimeUtc >= sourceInfo.LastAccessTimeUtc

And its usage:

Cp("source.big", "dest.big") if not IsUpToDate("source.big", "dest.big")

Or, you know what, this is fairly routine, and it comes as part of the standard library for Boobs. Let us create something new, ConditionalCopy:

def ConditionalCp(src as string, dest as string):
	Cp(src, dest) if not IsUpToDate(src, dest)

Usage should be clear by now, I hope.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
  2. Webinar (7):
    05 Jun 2025 - Think inside the database
  3. Recording (16):
    29 May 2025 - RavenDB's Upcoming Optimizations Deep Dive
  4. RavenDB News (2):
    02 May 2025 - May 2025
  5. Production Postmortem (52):
    07 Apr 2025 - The race condition in the interlock
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}