DSL Patterns - User Extensible Language

time to read 2 min | 356 words

One of the common mistakes in a DSL is to create one that solve a demo problem and leave it at that.

As a simple example, let us say that we build a DSL to handle order management. Here is a typical scenario that was talked about during development:

when is_preferred_user and order_amount > 500:
	apply_discount 5.precent

Sounds reasonable, right?

Except, that this isn't really a good way to express a business rule. A common business rule usually have a lot more complexity to it. Here is a good example:

when user.payment_method is credit_card and ((order_amount > 500 and order_amount < 1200) or number_of_payments < 4) and 
user.is_member_of("weekend buy club") and Now.DayOfWeek in (DayOfWeek.Sunday, DayOfWeek.Sutarday)
and applied_discounts < 10: apply_discount 5.precent

At a glance, what the !@$!# does this rule do?

This is a good example of stagnant DSL. It is no longer being actively developed (easily seen by the use of framework terms such as DayOfWeek), and the complexity is growing unchecked.

This is usually the case when the DSL design has not taken into account new functionality, or when it relies on developers to be able to extend the language when needed. Because it requires developer involvement, it is often easier to patch it by complex conditional rather than express the actual business logic in a readable way.

A good way to avoid that is to incorporate known best practices from the development side into our DSL. In other words, we need to allow encapsulation and extensibility in our DSLs. As a simple example, we can allow the following:

condition weekend_club_member:
	user.is_member_of("weekend club memeber")
condition sale_on_weekend:
	Now.DayOfWeek in (DayOfWeek.Sunday, DayOfWeek.Sutrday)
condition good_payment_option: # yes, I know, bad name, deal with it
	((order_amount > 500 and order_amount < 1200) or number_of_payments < 4)

And now the condition becomes:

when user.payment_method is credict_card and good_payment_option and sale_on_weekend 
and weekend_club_member and applied_discounts < 10: apply_discount 5.precent

This is important, because it avoid a language rot and provide the facilities to the end user to add abstraction levels.