Debasish specializes in leading delivery of enterprise scale solutions for various clients ranging from small ones to Fortune 500 companies. He is the technology evangelist of Anshin Software (http://www.anshinsoft.com) and takes pride in institutionalizing best practices in software design and programming. He loves to program in Java, Ruby, Erlang and Scala and has been trying desperately to get out of the unmanaged world of C++. Debasish is a DZone MVB and is not an employee of DZone and has posted 55 posts at DZone. You can read more from them at their website. View Full User Profile

Composing Heterogeneous DSLs in Scala

06.14.2011
| 3443 views |
  • submit to reddit

When we use DSLs to model business rules, we tend to use quite a few of them together. We may use a DSL for computing date/time, another one for manipulating money with currency, and a few others for implementing the actual rules of the domain. And not all of them will be developed by us - third party DSLs play an equally important role out here.

But when we use a bunch of heterogeneous DSLs, our application needs to be flexible enough to adapt to the independent evolution paths of each of them. We need to be able to embed entire DSLs within our application structure and yet not be coupled to the implementation of any of them.

In this blog post, I will demonstrate how we can organize heterogeneous DSLs hierarchically and achieve the above goals of keeping individual implementations decoupled from each other. This is also called representation independence and is yet another strategy of programming to an interface. I will use Scala as the implementation language and use the power of Scala's type system to compose these DSLs together.

The most interesting part of this implementation will be to ensure that the same representation of the composed DSL is used even when you extend your own DSL.

We are talking about accounts and how to compute the balance that a particular account holds. We have a method balanceOf that returns an abstraction named Balance. No idea about the implementation details of Balance though ..

trait Account {
val bal: Balances
import bal._

def balanceOf: Balance
}


Account is part of our own DSL. Note the abstract val Balances - it's a DSL which we embed within our own abstraction that helps you work with Balance. You can make a Balance abstraction, manipulate balances, change currencies etc. In short it's a utility general purpose DSL that we can frequently plug in to our application structure. Here we stack it hierarchically within our own DSL.

Here's how Balances look .

trait Balances {
type Balance

def balance(amount: Int, currency: String): Balance
def amount(b: Balance): Int
def currency(b: Balance): String
def convertTo(b: Balance, currency: String): Balance
}


Note Balance is abstracted within Balances. And we have a host of methods for manipulating a Balance.

Let's now have a look at a sample implementation of Balances, which also concretizes a Balance implementation ..

class BalancesImpl extends Balances {
case class BalanceImpl(amount: Int, currency: String)
type Balance = BalanceImpl

def balance(amount: Int, currency: String): Balance = {
BalanceImpl(amount, currency)
}

def amount(b: Balance) = b.amount
def currency(b: Balance) = b.currency

def convertTo(b: Balance, toCurrency: String): Balance = {
BalanceImpl(b.amount * 2, toCurrency)
}
}


Note Balances is a complete DSL totally decoupled from our own DSL that has the Account abstraction.

Now on to some concrete Account implementations ..

trait BankAccount extends Account {
// concrete implementation of Balances
val bal = new BalancesImpl

// object import syntax
import bal._

// dummy implementation but uses the Balances DSL
override def balanceOf = balance(10000, "USD")
}
object BankAccount extends BankAccount


It's a bank account that uses a concrete implementation of the Balances DSL. Note the balanceOf method uses the balance() method of the Balances DSL accessible through the object import syntax.

Now the fun part. My BankAccount uses one specific implementation of Balances. I would like to add a few decorators to my account, which will have richer versions of the API implementation. How do I ensure that all decorators that I may define for BankAccount also get to use the same DSL implementation for Balances?

Here's how .. Not only do we compose decorator and decoratee abstractions hierarchically, we use Scala's singleton type to ensure that the same representation of the Balances DSL gets to flow from the decoratee to the decorator.

// decorator
trait InterestBearing extends Account {
// decoratee
val semantics: Account

// singleton type pulls the same representation from up
val bal: semantics.bal.type

// object import ensures that any method of
// Balances that we use below comes from the same singleton type
import bal._

def interest: Int = 100 // dummy implementation

override def balanceOf = {
val b = semantics.balanceOf
balance(amount(b) + interest, currency(b))
}
}

So we have done a hierarchical composition of two heterogeneous DSLs and ensured that a single representation of one DSL is used uniformly within the other even in the face of extensions and decorations. The process has been made easy by the power of Scala's static type system.

Now we can have a concrete instance of an interest bearing bank account as a single Scala module ..

object InterestBearingBankAccount extends InterestBearing {
val semantics = BankAccount
val bal: semantics.bal.type = semantics.bal
}

 

From http://debasishg.blogspot.com/2011/06/composing-heterogeneous-dsls-in-scala.html

Published at DZone with permission of Debasish Ghosh, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)