Scala meetup - Milan, 25 May 2013
Molte caratteristiche di Scala sono state appositamente pensate (o almeno lo sembrano) per favorire la sviluppo di Domain Specific Languages. Ad esempio le conversioni implicite e le funzioni con più set di parametri è possibile progettare DSL interni molto leggibili, mentre i parser combinator consentono di sviluppare DSL esterni con un minimo sforzo. Lo scopo del talk è quello di mostrare il funzionamento di queste tecniche e strumenti con esempi pratici.
Driving Behavioral Change for Information Management through Data-Driven Gree...
Scala: the language of languages - Mario Fusco (Red Hat)
1. by Mario Fusco
Red Hat – Senior Software Engineer
mario.fusco@gmail.com
twitter: @mariofusco
Scala
the language
of languages
2. What is a
Domain Specific Language?
A
computer programming language
of
limited expressiveness
focused on a
particular domain
computer programming language
limited expressiveness
particular domain
4. The only purpose of
languages,
even programming ones
IS COMMUNICATION
Communication is king
5.
6. Written once, read many times
Always code as
if the person
who will
maintain your
code is a maniac
serial killer that
knows where
you live
7. "Any fool can write code
that a computer can
understand.
Good programmers
write code that humans
can understand“
Martin Fowler
8. Pros & Cons of DSLs
+ Expressivity Communicativity
+ Conciseness
+ Readability Maintainability Modificability
+ Higher level of abstraction
+ Higher productivity in the specific problem domain
̶ Language design is hard
̶ Upfront cost
̶ Additional indirection layer Performance concerns
̶ Lack of adeguate tool support
̶ Yet-another-language-to-learn syndrome
9. DSL taxonomy
External DSL a language having custom
syntactical rules separate from the main language of
the application it works with
Internal DSL a particular way of employing a
general-purpose language, using only a small subset
of the language's features
Language workbench a specialized IDE for
defining and building DSL, usually in a visual
way, used both to determine the structure of the
DSL and to provide an editing environment for
people using it
10. Internal DSL
External DSL
DSL Types Comparison
+ learning curve
+ cost of building
+ programmers familiarity
+ IDE support (autocompletion …)
+ composability
+ flexibility
+ readability
+ clear separation between
business (DSL) and host language
+ helpful when business code is
written by a separate team
(domain experts)
̶ syntactic noise
̶ needs to be recompiled
(if host language is static)
̶ need to learn of grammars
and language parsing
̶ boundary between DSL
and host language
̶ easier to grow out of
control
14. The golfers problem
• A foursome of golfers is standing at a tee, in a line from left to
right. Each golfer wears different colored pants; one is
wearing red pants.
• The golfer to Fred’s immediate right is wearing blue pants.
• Joe is second in line.
• Bob is wearing plaid pants.
• Tom isn’t in position one or four, and he isn’t wearing the
hideous orange pants.
• In what order will the four golfers tee off, and what color are
each golfer’s pants?”
15. The Hammurabi Solution (1)
var allPos = (1 to 4).toSet
var allColors =
Set("blue", "plaid", "red", "orange")
val assign = new {
def position(p: Int) = new {
def to(person: Person) = {
person.pos = p
allPos = availablePos - p
}
}
def color(c: String) = new {
def to(person: Person) = {
person.color = c
allColors = availableColors - c
}
}
}
class Person(n: String) {
val name = n
var pos: Int = _
var color: String = _
}
16. The Hammurabi Solution (2)
import hammurabi.Rule._
val ruleSet = Set(
rule ("Unique positions") let {
val p = any(kindOf[Person])
when {
(availablePos.size equals 1) and (p.pos equals 0)
} then {
assign position availablePos.head to p
}
},
[……]
rule ("Person to Fred’s immediate right is wearing blue pants") let {
val p1 = any(kindOf[Person])
val p2 = any(kindOf[Person])
when {
(p1.name equals "Fred") and (p2.pos equals p1.pos + 1)
} then {
assign color "blue" to p2
}
}
)
17. How Hammurabi DSL works (1)
case class Rule(description: String,
bind: () => RuleDefinition[_], salience: Int = 0)
case class RuleDefinition[A](condition: () => Boolean,
execution: () => A)
def rule(description: String) = new {
def let(letClause: => RuleDefinition[_]): Rule =
Rule(description, letClause _)
def withSalience(salience: Int) = new {
def let(letClause: => RuleDefinition[_]): Rule =
Rule(description, letClause _, salience)
}
}
rule ("An extremly useful rule") withSalience 5 let {
...
}
18. How Hammurabi DSL works (2)
def when(condition: => Boolean) = new {
def then[A](execution: => A): RuleDefinition =
RuleDefinition(condition _, execution _)
}
rule("Joe is in position 2") let {
val p = any(kindOf[Person])
when {
p.name equals "Joe"
} then {
assign position 2 to p
}
}
def ruleExecution() = {
val ruleDef = rule.bind()
if (ruleDef.condition()) ruleDef.execution()
}
20. A Scala Parser
abstract class Parser[+T] extends (Input => ParserResult[T])
trait Parsers {
sealed abstract class ParseResult[+T] { val next: Input }
case class Success[+T](result: T, override val next: Input)
extends ParseResult[T] { ... }
sealed abstract class NoSuccess(val msg: String,
override val next: Input)
extends ParseResult[Nothing] { ... }
case class Failure(override val msg: String,
override val next: Input)
extends NoSuccess(msg, next) { ... }
case class Error(override val msg: String,
override val next: Input)
extends NoSuccess(msg, next) { ... }
}
21. Parser Combinators
Combinator Symbol Description
Sequence ~ If two parsers P and Q are combined using the sequential
combinator, the parsing succeeds if:
■ P successfully consumes a part of the input stream
■ Q following P consumes the input that P did not consume
Alternation | A parser combinator for composing alternatives.
Selective
sequence
~>
<~
Selectively keep only either the right or the left result of a
sequence combinator
Repetition rep… A combinator that works on a parser P and returns another
parser that parses one or more repetitions of what P parses
Function
application
^^
^^^
A combinator that allows a function to be applied on a
parser, resulting in a new parser.
Variation Explanation
(rep(p), p*) Repeat p 0+ times
(rep1(p), p+) Repeat p 1+ times
(repN(n, p)) Repeat p exactly n times