Ayende @ Rahien

My name is Ayende Rahien
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:


+972 52-548-6969


Posts: 5,947 | Comments: 44,540

filter by tags archive

The magic of boo - Flexible syntax

when I am writing DSL, I keep hitting one pain point. The CLR naming conventions, which are more or less imprinted on my eyelids, are not really conductive to clear reading in a DSL.

Let us take these entities, and see what we get when we try to build a DSL from them:


The DSL is for defining business rules, and it looks like this:

when User.IsPreferred and Order.TotalCost > 1000:
	AddDiscountPrecentage  5
when not User.IsPreferred and Order.TotalCost > 1000:
when User.IsNotPreferred and Order.TotalCost > 500:

The main problem with this style of writing is that it is visually condense. I can read it pretty much as easily as I read natural English, but anyone who is not a developer really have to make an effort, and even for me, trying to read ruby styled code is easier. Here is how this would look like when using the ruby style conventions:

when User.is_preferred and Order.total_cost > 1000:
    add_discount_precentage 5
not User.is_preferred and Order.total_cost > 1000:
when User.is_not_preferred and Order.total_cost > 500:

This is much easier to read, in my opinion. The problem is that I consider this extremely ugly.


Obviously a different solution is needed...

Wait a minute! Boo has an open compiler. Why not just change the way it handle references? And that is what I did:

/// Allow to use underscore separated names, which will be translated to pascal case names.
/// pascal_case -> PascalCase.
/// All names that contains an underscores will go through this treatment.
/// <example>
/// You can  enable this behavior using the following statement
/// <code>
/// compiler.Parameters.Pipeline
///		.Replace(typeof (ProcessMethodBodiesWithDuckTyping),
/// 				 new ProcessMethodBodiesWithDslNamesAndDuckTyping());
/// </code>
/// </example>
public class ProcessMethodBodiesWithDslNamesAndDuckTyping : ProcessMethodBodiesWithDuckTyping
	/// <summary>
	/// Called when we encounter a reference expression
	/// </summary>
	/// <param name="node">The node.</param>
	public override void OnReferenceExpression(ReferenceExpression node)

	/// <summary>
	/// Called when we encounters a member reference expression
	/// </summary>
	/// <param name="node">The node.</param>
	public override void OnMemberReferenceExpression(MemberReferenceExpression node)
		if (node.Name.Contains("_"))

	/// <summary>
	/// Sets the node name to pascal case.
	/// </summary>
	/// <param name="node">The node.</param>
	private static void SetNodeNameToPascalCase(ReferenceExpression node)
		string[] parts = node.Name.Split(new char[] { '_' },StringSplitOptions.RemoveEmptyEntries);
		StringBuilder name = new StringBuilder();
		foreach (var part in parts)
		node.Name = name.ToString();

I love Boo, with cause.


grega g

very nice

i have been reading your blog for some time and i am increasingly interested in boo & dsl topic. But apart from seeing some dsl snippets, i would like an example how this fit into host application. Do you use compiler and compile it to memory? Maybe Interpreter? Or u make dll and reference it in host project.

What about User? This has to be some variable that is supplied from host than consumed by script, changed and than returned. How does one do that?


grega g

Eyad Salamin

What if you needed to have underscores in one of your properties, won't the compiler crash?

I guess it should try to find the exact match of the reference, and if not found try to replace underscores and "Pascalize" it.

Ayende Rahien


To answer your questions would take a book :-)

Luckily I am writing one of the topic.

You can get the information about it here:


Ayende Rahien


Personally, I never use underscores in my properties, so it is not a problem for me.

If you do have this issue, you would need to instruct the compiler in how to deal with this ambiguity

Chris Ortman

If I were writing it in ruby I would probably do:

add5percent and apply_freeshipping if user.preferred? and order.totalcost > 1000

Ayende Rahien

It is nice syntax, but I think it put too much information on one line.

Joe Gutierrez

What would happen if you instantiated, along with the the other business rules:

when User.is_preferred:


What happens to the other business rules? It would seem that it is possible to have more than one business rule apply.

If the first rule applies, then would this last rule apply. Are you're actions Idempotent?

Ayende Rahien

Yes, all the actions would apply.

Idempotent? I don't think so.

It can be done fairly easily, but I haven't thought about this

Joe Gutierrez

DSL's are normally declaritive. A challenge I see in this DSL is this:

Order.total_cost > 1000

This is more of a how than a what. If you add some properties to the the order class:

Size.Large, Size.Medium, Size.Small

I think it would be declarative this way. This would then equate to a sentence like this:

when User.is_preferred and Order.Size.Large:

add_discount_precentage 5


Maybe change the action to read:

when User.is_preferred and Order.Size.Large:



You could add another operation to your actions:

apply_discount 2.5

As a side note, you have a total of 6 combinations of words. The total of original actions that can be created equals 8 different messages. What is up with that?

Another challenge is the Order sizes. If implemented as wriiten in the original rules you may run into some problems like:

when User.is_preferred and Order.Size.Large and not Order.Size.Medium:

This is some pretty exciting stuff! Keep it coming.

Ayende Rahien


I am not following about the combination of words.

About Size.Large, I would probably define a bunch of extension methods, so I could do things like:

Order.ApplicableForPrefferedShipping, or, actually:


Joe Gutierrez

You have two states for input User:



You have three states for Size of order:

total_cost > 1000

total_cost > 500

(whatever isn't covered by the previous states

2*3 = 6 input states

Output states is three

no transactions to all three transaction

2-E3 = 8 states. Due to the number of input states you will not be able to use all 8 states. Of course, some states will be invalid.

What seems to be exciting is that you can create a formal grammar from the set of rules you've implemented.

The real trick that I really haven't seen, but I think that would be really cool is adding a compiling step (annotations?) to the business rules and not let you compile the program if you create a business rule that isn't part of the formal grammar.

The above would be a unit test on possible grammars?


when User.IsPreferred and Order.TotalCost > 1000:



Business Rule Compile Error: Customer is already preferred, no upgrade required.

Ayende Rahien


That is because you are seeing the simplest possible example.

Try to imagine this on a real world system, where you may have tens or hundreds of variables.

In this scenario, trying to define a rigid system breaks.

Comment preview

Comments have been closed on this topic.


No future posts left, oh my!


  1. RavenDB Sharding (2):
    21 May 2015 - Adding a new shard to an existing cluster, the easy way
  2. The RavenDB Comic Strip (2):
    20 May 2015 - Part II – a team in trouble!
  3. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  4. Interview question (2):
    30 Mar 2015 - fix the index
  5. Excerpts from the RavenDB Performance team report (20):
    20 Feb 2015 - Optimizing Compare – The circle of life (a post-mortem)
View all series



Main feed Feed Stats
Comments feed   Comments Feed Stats