offers abstractions at a higher level.
Therefore, it is important the language you use to implement your DSL
offers similar abstraction capabilities.
The more expressive the language is,
the less will be the semantic gap between the native abstractions of the
language and the custom abstractions
you build over it for your DSL. When
you choose a language for embedding
your DSL, keep an eye on the level of
abstractions it offers.
Let’s consider an example of designing a small DSL for a specific
domain using Scala as the host language. Scala2, 5 is an object functional
language designed by Martin Odersky
and offers a host of functional and object-oriented features for abstraction
design. It has a flexible syntax with
type inferencing, an extensible object
system, a decent module system, and
powerful functional programming
capabilities that enable easier development of expressive DSLs. Other
features that make Scala a suitable
language for embedding DSLs include
lexically scoped open classes, implicit
parameters, and statically checked
duck typing capabilities using structural types. 2
The problem domain. This example involves a business rule from
the domain of securities trading operations, where traders buy and sell
securities in a stock exchange (also
known as the market) on behalf of
their clients, based on some placed
order. A client order is executed in
the exchange and generates a trade.
Depending on whether it is a buy or a
sell trade, cash is exchanged between
the client and the trader. The amount
of cash exchanged is referred to as the
net cash value of the trade and varies
with the market where the trade is executed.
The business rule used in our example determines the cash-value computation strategy for a specific trade. We
built a DSL on top of the core abstractions of the domain model that makes
the business rules explicit within the
program and can be easily verified by
the business users. The core abstractions shown here are simplified for
demonstration purposes; the actual
production-level abstractions would
be much more detailed and complex.
The main idea is to show how DSLs
A DSL offers
specialized
syntactic constructs
that model
the daily language
of a business user.
can be embedded within a powerful
language such as Scala to offer domain-friendly APIs to users.
The solution domain model. The
domain model offers the core abstractions of the business. In our example
we use the power of algebraic data
types in Scala to model some of the
main objects. Trade is the primary abstraction of the domain. Here’s how it
is modeled using Scala case classes:
case class Trade(
account: Account,
instrument: Instrument,
refNo: String,
market: Market,
unitPrice: BigDecimal,
quantity: BigDecimal,
tradeDate: Date = Calendar.get-Instance.getTime,
valueDate: Option[Date] = None,
taxFees:Option[List[(TaxFeeId,
BigDecimal)]] = None,
netAmount: Option[BigDecimal]
= None)
In reality, a trade abstraction will
have many more details. Similar to
Trade, we can also use case classes to
implement abstractions for Account
and Instrument. We elide them for
the time being, as their detailed implementations may not be relevant in
this context.
Another abstraction we will use
here is Market, also kept simple for
the example:
sealed trait Market
case object HongKong extends
Market
case object Singapore extends
Market
case object New York extends Market
case object Tokyo extends Market
These examples use case classes
for algebraic data types and case objects to model singletons. Scala case
classes offer a few nice features that
make the code succinct and concise:
˲ ˲ Constructor parameters as public
fields of the class
˲˲Default implementations of
equals, toString, and hashCode
based on constructor fields
˲ ˲A companion object containing
an apply() method and an extractor
based on constructor fields