DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Languages
  4. Rolling Your Own DSL in Scala

Rolling Your Own DSL in Scala

Getting started with your own domain-specific languages is easy with Scala. This guide walks you through the basics of creating a DSL for your own use.

Tarek Ahmed user avatar by
Tarek Ahmed
·
Mar. 13, 17 · News
Like (9)
Save
Tweet
Share
22.10K Views

Join the DZone community and get the full member experience.

Join For Free

Creating your own DSL in Scala is very easy. You don't need language proficiency at an expert level for this. This post shows you how to get started. It is not our scope to teach basic concepts of Scala, like traits and objects, but even if you are not familiar with the language, it should be possible to follow the logic here.

I have been dabbling in Scala for some time. At my place of work, though, it does not seem feasible to use Scala for code in production, so I decided that I could get away with using it in unit testing at least. Since I am of the school of total white-box-testing, I do not hesitate to set and access private members of classes whenever it saves a clean design from being disrupted. I know doing so is a bone of contention, but this is not what this post is about – it’s what the DSL is about.

In Java, as in Scala, accessing private class members by reflection is quite tedious and repetitive, so I decided to write a utility for that. And, using Scala, I had something like this in mind:

val bar = new Bar
set field “foo” of bar to “baz”
val qux = value of method “someInternalMethod” of bar


Looks nice, right? This would make use of the fact that Scala allows one-argument methods without dot operator and parentheses. So, how to go about it? Regarding Scala, I would characterize myself as an advanced beginner, so my toolset does not include any arcane stuff that only gurus understand. But wait, this can't be so difficult. Let’s take it piece by piece.

set field “foo” of bar to “baz”


Ok, to begin with, I need something called set. This should probably be an instance of some class. How do I get it into my unit test class? Well, a trait should do fine.

trait Decapsulation {
  val set = new Set()
}


Then I can use it like this:

@RunWith(classOf[JUnitRunner])
class BarTest extends FlatSpec with Decapsulation {
  "A bar" should "foo" in {
    val bar = new Bar
    set field "foo" of bar to "baz"
    // do more stuff and assert something now
    // ...
  }
}


For those not familiar with Scala, this exemplifies the structure of a FlatSpec-based, behavior-driven test.

It looks like set has a method called field , and this method takes a string parameter (“foo” in the example). Since there seems to be only one instance of set necessary, we can make it a Scala object instead of a class.

object Set {
  def field(name: String): ??? = ???
}


Then the trait can look like this:

trait Decapsulation {
  val set = Set
}


The field method must return something that has a method called of. And this method takes any type of object as a parameter (the object under test bar in our case). Right, let’s create a class Of (call it whatever you like, it does not matter for the DSL. What matters is the name of the method):

class Of(val name: String) {
  def of(o: AnyRef): ??? = ???
}


We will see in a minute that we need different instances of Of, so Of needs to be a class, not a Scala object. The field method returns an instance of an Of. On this instance we call the method (lowercase) of. The field method looks like this now:

def field(name: String): Of = new Of(name) 


Remember – name is the name of the field we are going to manipulate and we hand that over to the new instance of Of. Now, the of method needs to return something we can use to call to on. Well, I guess, you get the hang of it by now ...

class To(name: String, obj: AnyRef) {
  def to(value: Any):??? = ???
}


This makes the of method look like this:

def of(obj: AnyRef): To = new To(name, obj)


We pass our collected arguments (field name and the object containing the field) to a new To instance. The (lowercase) to method can get the new value for our field as the single parameter. And since we now have all we need, we can do the tedious reflection call and actually set the field:

def to(value: Any): Unit = {
  val f = obj.getClass.getDeclaredField(name)
  f.setAccessible(true)
  f.set(obj, value)
}


That’s it, really – this is all there is to get started on a DSL in Scala. In hindsight, it looks almost trivially easy. The example is highly simplified – the field might belong to a base class or be final or static or both or whatever – but all this can be handled in gory detail in the to method.

By the way, the DSL works in Java, too:

@Test
public void test() {
    Bar bar = new Bar();
    Set.field("foo").of(bar).to("baz");
    // do more stuff and assert something now
    // ...
}


Just import the Set class — field  is a static method of the class.

In conclusion, there are these principles at work:

  • Start with the instance of a class or a Scala object that represents the first instruction.
  • Use one class for each instruction so the user is guided through the statement and autocompletion in IDEs works correctly. The class contains a method (or several methods, if there are branches), that represents the current instruction.
  • The methods take arguments if necessary. If each method takes maximally one argument, there is no need for parentheses and the DSL looks rather like a natural language.
  • Pass through all data you collect as constructor arguments to the next class on the way to the final instruction in the DSL statement and process them there.

The complete working code is right here. Paste it into a Scala file and test it.

package getting.started.on.a.dsl.in.scala

trait Decapsulation { val set = Set }

object Set { def field(name: String): Of = new Of(name) }

class Of(val name: String) { def of(obj: AnyRef): To = new To(name, obj) }

class To(name: String, obj: AnyRef) {
  def to(value: Any): Unit = {
    val f = obj.getClass.getDeclaredField(name)
    f.setAccessible(true)
    f.set(obj, value)
  }
}


I leave it to you to figure out how to implement:

val qux = value of method “someInternalMethod” of bar


It should not be difficult after the field example. One hint though — you'll need to call the container class for the new of method something other than Of, or put it in another package than the field's Of class. And probably generics need to come into play to make the instruction return objects of the right type instead of Nothing.

Domain-Specific Language Scala (programming language) unit test Object (computer science)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • All the Cloud’s a Stage and All the WebAssembly Modules Merely Actors
  • Software Maintenance Models
  • Readability in the Test: Exploring the JUnitParams
  • Distributed Tracing: A Full Guide

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: