Writing an internal DSL in scala


Jiří Kremser





2014-02-07

Outline


  • Scala basics
  • External/Internal DSL
  • Scala DSL-friendly features
  • Coding
  • ..more coding
  • ..even more coding

Motivation

import collection.mutable.Stack
import org.scalatest._

class ExampleSpec extends FlatSpec with Matchers {

  "A Stack" should "pop values in last-in-first-out order" in {
    val stack = new Stack[Int]
    stack.push(1)
    stack.push(2)
    stack.pop() should be (2)
    stack.pop() should be (1)
  }

  it should "throw NoSuchElementException if an empty stack is popped" in {
    val emptyStack = new Stack[Int]
    a [NoSuchElementException] should be thrownBy {
      emptyStack.pop()
    } 
  }
}

Scala basics


  • JVM based
  • Compiled to bytecode
  • OOP
  • Functional paradigm
  • Type safety
  • "SCAlable LAnguage"

Scala vs Java

Scala                                             Java

variables
val foo = "Hello Scala!"
var counter = 42
public final String foo = "Hi Java!";
public int counter = 42;

methods
def multiply(a: Int, b: Int) = a * b
public int multiply(int a, int b) {
   return a * b;
}

Scala vs Java

Scala                                             Java

classes
case class Dog(val name: String)

    + equals() & hashcode()

    + toString()

    + pattern matching

public class Dog {
   private final String name;
   
   public Dog(String name) {
      this.name = name;
   }
   
   public getName() {
      return name;
   }
}

Higher order functions

val evenNums = (1 to 1000).filter(x => x % 2 == 0)

val evenNums = (1 to 1000).filter(_ % 2 == 0)

def isEven = _ % 2 == 0
val evenNums = (1 to 1000) filter isEven

val evenNums = for (i <- 1 to 1000; if i % 2 == 0) yield i)

val evenNums = for (i <- 1 to 500) yield 2 * i)

val evenNums = 2 to 1000 by 2
def foo(f1: (String => Int), f2: (Int => String)) = f1 andThen f2

val obfuscate = foo(_.length, "*" * _)

Domain specific language


  • serves one purpose (is not Turing-complete)
  • language implies communication
  • useful for higher lvl abstraction
  • well designed DSL has great marketing value
  • External vs Internal DSLs
  • from fluent API to natural language

     bind(String.class)
        .annotatedWith(Names.named("JDBC URL"))
        .toInstance("jdbc:mysql://localhost/pizza");

External dsl


  • CSS,  SQL, SPARQL, "*QL", etc.
  • compiled / interpreted
  • requires some toolkit to design the lang.
  • ANTLR, IntelliJ MPS, Eclipse Xtext
  • programs represented as strings / XMLs / JSONs
  • mostly, no compile time validation

Internal DSL


  • language constructs = host language
  • host lang. is type safe → DSL may be type safe
  • fluent APIs (builder pattern)
  • suitable languages: Scala, Ruby, Clojure (Lisp), Groovy
  • good practice: no side effects, just build an object
  • ..then pass the object to a method (M. Fowler)

Scala and DSL


  • Implicit conversions
class IntWrapper(val value: Int) {
  def C = s"$value degrees" // same as value + "degrees"
}
implicit def Int2IntWrapper(value: Int) = new IntWrapper(value)

  • Anonymous classes
(new {
  def foo() = "bar"
}).foo()

Scala and DSL


  • Method calling
one argument
obj.method(arg)
obj method(arg)
obj.method (arg)
obj method (arg)
obj method arg

no argument
obj.method()
obj method()
obj.method
obj method

Scala and DSL


  • Method calling again
precedence rules
foo bar baz bam
foo bar baz bam bim
foo bar baz bam bim bom
foo bar baz bam bim bom grr
(foo.bar(baz)).bam
(foo.bar(baz)).bam(bim)
((foo.bar(baz)).bam(bim)).bom
((foo.bar(baz)).bam(bim)).bom(grr)

Scala and DSL





Talk is cheap, let's code


Scala Installation

Scala Hello Wold App
 println("Hello World!")


...

Thats all folks

Feedback

This presentation

Writing an internal DSL

By jkremser

Writing an internal DSL

  • 808