Ayende @ Rahien

Refunds available at head office

Boo: Design By Contract in 20 lines of code

Now, before Greg hurls a modopt on me, I want to be clear that this isn't the same thing that Spec# is doing. But it is a very cool way to specify constraints that must always be valid when a method exists.

Here is the code:

[AttributeUsage(AttributeTargets.Class)]
class EnsureAttribute(AbstractAstAttribute):
	
	expr as Expression
	
	def constructor(expr as Expression):
		self.expr = expr
		
	def Apply(target as Node):
		type as ClassDefinition = target
		for member in type.Members:
			method = member as Method
			continue if method is null
			block = method.Body
			method.Body = [|
				block:
					try:
						$block
					ensure:
						assert $expr
			|].Block

And the usage:

[ensure(name is not null)]
class Customer:
	name as string
		
	def constructor(name as string):
		self.name = name
	
	def SetName(newName as string):
		name = newName

Now, any attempt to set the name to null will cause an assertion exception. This technique is quite powerful, and very easy to use. A few years ago I wrote a design by contract implementation for boo that was far more ambitious (handling inheritance, etc). I remember it being much more complicated, and while things like quasi quotation do make it easier, it is not that big a change.

I think that mostly it is the way I write code now, striving to simplicity is something that I am trying to apply recently, and I think it works.

Comments

Bill Barry
12/22/2007 05:08 AM by
Bill Barry

wow, that right there is basically what I just said on the alt.net list is a capability I wish would be available for C# 4.0.

How do you come up with this stuff so fast?

Ayende Rahien
12/22/2007 07:39 AM by
Ayende Rahien

Bill,

Take a look at the code and you'll understand how I come with it so fast. It is simple, when you know how.

Bill Barry
12/22/2007 01:39 PM by
Bill Barry

That I realize, I was referring to the "person X thinks about it, you have already made a blog post about it" concept. It is like you are in all of our heads.

(In this case your post came before my comment on the list, I just hadn't seen it yet.)

Tobin Harris
12/22/2007 02:54 PM by
Tobin Harris

That looks cool! I guess it would be easy to put them on methods too, such as:

[invariant(name is not null)]

class Customer:

[precondition(orders is not empty)], [postcondition(discount is not null)]

def ApplyDiscount():

Ayende Rahien
12/22/2007 03:14 PM by
Ayende Rahien

Tobin,

Yes, you can put this on methods as well.

Joe Gutierrez
12/22/2007 04:29 PM by
Joe Gutierrez

How about something like this:

[ensure(name is not null) and (name is not empty)]

?

concatenations of operators?

Ayende Rahien
12/22/2007 04:56 PM by
Ayende Rahien

This will work:

[ensure(name is not null and name is not empty)]

Joe Gutierrez
12/25/2007 12:51 AM by
Joe Gutierrez

Do you have unit tests for the dynamic code?

Robert Taylor
12/26/2007 02:41 PM by
Robert Taylor

When is EnsureAttribute.Apply(...) called? and by what?

Ayende Rahien
12/26/2007 02:46 PM by
Ayende Rahien

During compilation, by the compiler

Robert Taylor
12/26/2007 03:09 PM by
Robert Taylor

I'm guessing that Apply is inherited from AbstractAstAttribute and the compiler has a "built-in rule" to call Apply on all AbstractAstAttributes?

Comments have been closed on this topic.