More Related Content Similar to GroovyDSLs (20) GroovyDSLs2. Topics
DSL Origins
• Groovy Intro
• Groovy DSL Features
• Groovy DSL Examples
• Advanced Topics & Guidelines
© ASERT 2006-2010
• More Info
DSLs 2010 - 2
3. What is a DSL?...
• A domain-specific language is a
programming language or executable
specification language that offers, through
appropriate notations and abstractions,
expressive power focused on, and usually
© ASERT 2006-2010
restricted to, a particular problem domain
– In contrast, general-purpose languages are created to solve
problems in many domains
– Somewhere between declarative data and a full blown general-
purpose programming language (GPL)
– AKA: fluent / human interfaces, language oriented
programming, problem-oriented languages, little / mini
languages, macros, business natural languages
Sources:
http://en.wikipedia.org/wiki/Domain-specific_language
van Deursen, A., Klint, P., Visser, J.: Domain-specific languages: an annotated bibliography. ACM SIGPLAN Notices 35 (2000) 26–36
4. ...What is a DSL?
• Advantages: • Disadvantages:
– Domain experts can – Learning cost vs. limited
understand, validate, modify, applicability
and often even develop DSL – Cost of designing,
programs implementing & maintaining
– Somewhat self-documenting DSL as well as the tools/IDEs
– Enhance quality, – Attaining proper scope
© ASERT 2006-2010
productivity, reliability, – Trade-offs between domain-
maintainability, portability specificity and general-
and reusability purpose programming
– Safety; as long as the language constructs
language constructs are safe – Efficiency costs
any sentence written with – Proliferation of similar non-
them can be considered safe standard DSLs, i.e. different
but similar DSLs used within
two insurance companies
Source: http://en.wikipedia.org/wiki/Domain-specific_language DSLs 2010 - 4
5. Origins
• It has always been a
holy grail of computer
users to be able to
speak directly to our
computers
© ASERT 2006-2010
• Different languages have pushed the
boundaries further than others
– APT for numerically controlled machine tools (1957)
– BNF (1959), COBOL, 4GLs
– LISP & Smalltalk
– Unix "little languages"
DSLs 2010 - 5
6. Origins
• It has always been a
holy grail of computer
users to be able to
speak directly to our
computers
© ASERT 2006-2010
http://dsal.uchicago.edu/reference/gazetteer/pager.html?objectid=DS405.1.I34_V02_298.gif
DSLs 2010 - 6
7. Origins: LISP & Smalltalk
“In Lisp, you don’t just write your
program down toward the language,
you also build the language up
toward your program”
- Paul Graham
© ASERT 2006-2010
“When [Smalltalk] is used to
describe an application system, the
developer extends Smalltalk,
creating a domain-specific language
by adding a new vocabulary of
language elements ...”
- Adele Goldberg
DSLs 2010 - 7
8. Origins: Minilanguages...
Source: The Art of Unix Programming:Taxonomy of languages: http://www.faqs.org/docs/artu/ch08s01.html
# Minilanguage taxonomy %!PS-Adobe-3.0
# Base ellipses %%Creator: groff version 1.20.1
define smallellipse {ellipse width 3.0 height 1.5} ...
M: ellipse width 3.0 height 1.8 fill 0.2 597.6 12 72 12 DL(increasing loopiness)297.71 8.2 Q(/etc/passwd)102.67
line from M.n to M.s dashed 94.6 Q(.ne)110.715 106.6 Q(wsrc)-.25 E(SNG)195.2 100.6 Q(re)243.8 94.6 Q
D: smallellipse() with .e at M.w + (0.8, 0) (ge)-.15 E(xps)-.15 E(Glade)247.26 106.6 Q(m4)306.81 58.6 Q -1(Ya)303.43
line from D.n to D.s dashed 70.6 S(cc)1 E(Le)305.5 82.6 Q(x)-.15 E(mak)302.42 94.6 Q(e)-.1 E(XSL)
I: smallellipse() with .w at M.e - (0.8, 0) 301.16 106.6 Q(T)-.92 E(pic)307.09 118.6 Q(tbl)307.92 130.6 Q(eqn)305.98
142.6 Q(fetchmail)344.715 82.6 Q -.15(aw)355.345 94.6 S(k).15 E(trof)
# Arrow headings 354.84 106.6 Q(f)-.25 E(Postscript)343.875 118.6 Q(dc)412.88 94.6 Q(bc)
arrow from D.w + (0.4, 0.8) to D.e + (-0.4, 0.8) 412.88 106.6 Q(Emacs Lisp)462.53 94.6 Q(Ja)465.395 106.6 Q -.25(va)-.2 G
"flat to structured" "" at last arrow.c (Script).25 E(sh)529.075 94.6 Q(tcl)528.52 106.6 Q(Perl)565.065 88.6 Q
... (Python)558.95 100.6 Q(Ja)564.46 112.6 Q -.25(va)-.2 G 0 Cg EP
# Interpreters %%Trailer
"Emacs Lisp" "JavaScript" at 0.25 between M.e and I.e end
"sh" "tcl" at 0.55 between M.e and I.e %%EOF
"Perl" "Python" "Java" at 0.8 between M.e and I.e
Input DSL: pic for above picture Output "DSL": Postscript for above picture DSLs 2010 - 8
9. ...Origins: Minilanguages...
import builder.PowerPointBuilder
def name = 'minilanguages'
assert new File("${name}.pic').exists()
"groff -e -p ${name}.pic > ${name}.ps".execute()
"gs -q -sDEVICE='ppmraw' -g2600x3500 -r300x300 -sOutputFile='-' -dBATCH ↵
–dNOPAUSE ${name}.ps | pnmcrop | ppmtogif > ${name}.gif".execute()
def builder = new PowerPointBuilder() // Adapted from Erik Pragt
builder.slideshow(filename: 'DSLsInGroovy_MiniLanguages.ppt') {
slide(title: 'Origins: Minilanguages...') {
image(
origin: [0, 15],
src: "${name}.gif",
caption: 'Source: The Art of Unix Programming:Taxonomy...' )
textbox(
origin: [5, 100],
text: new File("${name}.pic").text,
caption: 'Input DSL: pic for above picture' )
textbox(
origin: [115, 100],
text: new File("${name}.ps").text,
caption: 'Output "DSL": Postscript for above picture' )
}
}
PowerPoint DSL for previous slide DSLs 2010 - 9
10. ...Origins: Minilanguages
Glade XSLT
<?xml version="1.0"?> <?xml version="1.0"?>
<GTK-Interface> <xsl:stylesheetversion="1.0"
<widget> xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<class>GtkWindow</class> <xsl:output method="xml"/>
<name>HelloWindow</name> <xsl:template match="*">
<border_width>5</border_width> <xsl:element name="{name()}">
<Signal> <xsl:for-each select="@*">
<name>destroy</name> <xsl:element name="{name()}">
<handler>gtk_main_quit</handler> <xsl:value-of select="."/>
</Signal> </xsl:element>
<title>Hello</title> </xsl:for-each>
<type>GTK_WINDOW_TOPLEVEL</type> <xsl:apply-templates select="*|text()"/>
<position>GTK_WIN_POS_NONE</position> </xsl:element>
<allow_shrink>True</allow_shrink> </xsl:template>
<allow_grow>True</allow_grow> </xsl:stylesheet>
<auto_shrink>False</auto_shrink> Regex
<widget>
<class>GtkButton</class> "x.z?z{1,3}y"
<name>Hello World</name>
<can_focus>True</can_focus> fetchmail
<label>Hello World</label>
</widget> # Poll this site first each cycle.
poll pop.provider.net proto pop3
</widget>
</GTK-Interface> user "jsmith" with pass "secret1" is "smith" here
user jones with pass "secret2" is "jjones" here with options keep
SQL # Poll this site second, unless Lord Voldemort zaps us first.
poll billywig.hogwarts.com with proto imap:
SELECT * FROM TABLE user harry_potter with pass "floo" is harry_potter here
WHERE NAME LIKE '%SMI' # Poll this site third in the cycle.
ORDER BY NAME # Password will be fetched from ~/.netrc
poll mailhost.net with proto imap:
user esr is esr here
Troff
cat thesis.ms | chem | tbl | refer | grap | pic | eqn | groff -Tps > thesis.ps
Source: Applying minilanguages: http://www.faqs.org/docs/artu/ch08s02.html DSLs 2010 - 10
13. State of the Art for DSLs?
© ASERT 2006-2010
Source: http://www.infoq.com/presentations/vanderburg-state-of-dsl-ruby – Gartner's take on technology adoption DSLs 2010 - 13
14. "Is it a DSL or an API?" Checklist
• A ten-question test to help determine whether
a wad of code represents a DSL or an API:
1. Have you ever programmed in a language other than Ruby?
(PHP and HTML don‟t count.) If not, it‟s a DSL.
2. Is the defining syntactic feature that you’ve cleverly left the
parentheses off of a list of function arguments?
If so, it‟s a DSL.
3. Is the code primarily a list of key-value pairs?
Welcome to DSL Town, population you!
4. Does the code require the liberal use of eval() or equivalent?
DSL, yay!
5. Have you ever used the phrase “… and it reads just like
English!” in seriousness? You‟d better get to the hospital;
you‟re coming down with a case of the DSLs!
6. ...
Source: http://www.oreillynet.com/onlamp/blog/2007/05/the_is_it_a_dsl_or_an_api_ten.html DSLs 2010 - 14
15. DSL usage patterns...
• Internal (uses a general purpose language)
– AKA embedded
– Can be a subset or superset
– Can be generated/converted from other "language"
– Numerous mechanisms to make as close to the
domain as possible
© ASERT 2006-2010
• External (custom language)
– May involve writing your own traditional parser
– Using parser combinators
– Interpreting
– String grepping
• Other characteristics
– Functional vs OO
DSLs 2010 - 15
17. ...DSL usage patterns
© ASERT 2006-2010
Source: DSLs in Action, Debasish Ghosh, Manning now in MEAP DSLs 2010 - 17
18. DSL usage patterns (Advanced)
© ASERT 2006-2010
Source: http://www.spinellis.gr/pubs/jrnl/2000-JSS-DSLPatterns/html/dslpat.html
See: Diomidis Spinellis. Notable design patterns for domain specific languages. Journal of Systems and Software, 56(1):91–99, February 2001.
19. Topics
• DSL origins
Groovy Intro
• Groovy DSL Features
• Groovy DSL Examples
• Advanced Topics & Guidelines
© ASERT 2006-2010
• More Info
DSLs 2010 - 19
20. What is Groovy?
• “Groovy is like a super version
of Java. It can leverage Java's
enterprise capabilities but also
has cool productivity features like closures,
DSL support, builders and dynamic typing.”
© ASERT 2006-2010
Groovy = Java – boiler plate code
+ optional dynamic typing
+ closures
+ domain specific languages
+ builders
+ metaprogramming
DSLs 2010 - 20
21. Groovy Goodies Overview
• Fully object oriented
• Closures: reusable
and assignable
pieces of code
• Operators can be • GPath: efficient
overloaded
© ASERT 2006-2010
object navigation
• Multimethods • GroovyBeans
• Literal declaration for • grep and switch
lists (arrays), maps,
ranges and regular • Templates, builder,
expressions swing, Ant, markup,
XML, SQL, XML-RPC,
Scriptom, Grails,
tests, Mocks DSLs 2010 - 21
22. Growing Acceptance …
A slow and steady start but now gaining in
momentum, maturity and mindshare
Now free
24. … Growing Acceptance …
© ASERT 2006-2010
Groovy and Grails downloads:
70-90K per month and growing DSLs 2010 - 24
25. … Growing Acceptance …
© ASERT 2006-2010
Source: http://www.micropoll.com/akira/mpresult/501697-116746
Source: http://www.grailspodcast.com/
DSLs 2010 - 25
26. … Growing Acceptance …
© ASERT 2006-2010
http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes
http://www.java.net
DSLs 2010 - 26
27. … Growing Acceptance …
What alternative JVM language are you using or intending to use
© ASERT 2006-2010
http://www.leonardoborges.com/writings
DSLs 2010 - 27
28. … Growing Acceptance …
© ASERT 2006-2010
http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com)
DSLs 2010 - 28
30. The Landscape of JVM Languages
optional
static
types
© ASERT 2006-2010
Dynamic features call
for dynamic types Java bytecode calls
for static types
The terms “Java Virtual Machine” and “JVM” mean a Virtual Machine for the Java™ platform.
DSLs 2010 - 30
31. Groovy Starter
System.out.println("Hello, World!"); // supports Java syntax
println 'Hello, World!' // but can remove some syntax
String name = 'Guillaume' // Explicit typing/awareness
println "$name, I'll get the car." // Gstring (interpolation)
def longer = """${name}, the car
is in the next row.""" // multi-line, implicit type
assert 0.5 == 1/2 // BigDecimal equals()
© ASERT 2006-2010
assert 0.1 + 0.2 == 0.3 // and arithmetic
def printSize(obj) { // implicit/duck typing
print obj?.size() // safe dereferencing
}
def pets = ['ant', 'bee', 'cat'] // native list syntax
pets.each { pet -> // closure support
assert pet < 'dog' // overloading '<' on String
} // or: for (pet in pets)...
DSLs 2010 - 31
32. A Better Java...
import java.util.List;
import java.util.ArrayList;
class Erase {
private List removeLongerThan(List strings, int length) { This code
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) { is valid
String s = (String) strings.get(i);
if (s.length() <= length) { Java and
}
result.add(s); valid Groovy
}
return result;
© ASERT 2006-2010
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Erase e = new Erase(); Based on an
List shortNames = e.removeLongerThan(names, 3);
System.out.println(shortNames.size()); example by
for (int i = 0; i < shortNames.size(); i++) { Jim Weirich
String s = (String) shortNames.get(i);
System.out.println(s); & Ted Leung
}
}
}
DSLs 2010 - 32
33. ...A Better Java...
import java.util.List;
import java.util.ArrayList;
class Erase {
private List removeLongerThan(List strings, int length) { Do the
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) { semicolons
String s = (String) strings.get(i);
if (s.length() <= length) { add anything?
}
result.add(s); And shouldn‟t
} we us more
return result;
© ASERT 2006-2010
} modern list
public static void main(String[] args) {
List names = new ArrayList(); notation?
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
Why not
System.out.println(names); import common
Erase e = new Erase();
List shortNames = e.removeLongerThan(names, 3); libraries?
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
DSLs 2010 - 33
34. ...A Better Java...
class Erase {
private List removeLongerThan(List strings, int length) {
List result = new ArrayList()
for (String s in strings) {
if (s.length() <= length) {
result.add(s)
}
}
return result
}
public static void main(String[] args) {
List names = new ArrayList()
© ASERT 2006-2010
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
Erase e = new Erase()
List shortNames = e.removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (String s in shortNames) {
System.out.println(s)
}
}
}
DSLs 2010 - 34
35. ...A Better Java...
class Erase {
private List removeLongerThan(List strings, int length) {
List result = new ArrayList()
for (String s in strings) {
if (s.length() <= length) {
result.add(s) Do we need
}
} the static types?
}
return result Must we always
have a main
public static void main(String[] args) {
List names = new ArrayList() method and
© ASERT 2006-2010
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned") class definition?
System.out.println(names)
Erase e = new Erase()
How about
List shortNames = e.removeLongerThan(names, 3) improved
System.out.println(shortNames.size())
for (String s in shortNames) { consistency?
System.out.println(s)
}
}
}
DSLs 2010 - 35
36. ...A Better Java...
def removeLongerThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() <= length) {
result.add(s)
}
}
return result
}
© ASERT 2006-2010
names = new ArrayList()
names.add("Ted")
names.add("Fred")
names.add("Jed")
names.add("Ned")
System.out.println(names)
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
DSLs 2010 - 36
37. ...A Better Java...
def removeLongerThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() <= length) {
result.add(s) Shouldn‟t we
}
} have special
return result notation for lists?
}
And special
© ASERT 2006-2010
names = new ArrayList() facilities for
names.add("Ted")
names.add("Fred")
list processing?
names.add("Jed") Is „return‟
names.add("Ned") needed at end?
System.out.println(names)
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
DSLs 2010 - 37
38. ...A Better Java...
def removeLongerThan(strings, length) {
strings.findAll{ it.size() <= length }
}
names = ["Ted", "Fred", "Jed", "Ned"]
System.out.println(names)
shortNames = removeLongerThan(names, 3)
System.out.println(shortNames.size())
shortNames.each{ System.out.println(s) }
© ASERT 2006-2010
DSLs 2010 - 38
39. ...A Better Java...
def removeLongerThan(strings, length) {
strings.findAll{ it.size() <= length }
}
Is the method
names = ["Ted", "Fred", "Jed", "Ned"] now needed?
System.out.println(names)
shortNames = removeLongerThan(names, 3) Easier ways to
System.out.println(shortNames.size()) use common
shortNames.each{ System.out.println(s) }
methods?
© ASERT 2006-2010
Are brackets
required here?
DSLs 2010 - 39
40. ...A Better Java...
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() <= 3 }
println shortNames.size()
shortNames.each{ println it }
© ASERT 2006-2010
DSLs 2010 - 40
41. ...A Better Java
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() <= 3 }
println shortNames.size()
shortNames.each{ println it }
© ASERT 2006-2010
["Ted", "Fred", "Jed", "Ned"]
3
Ted
Jed
Ned
DSLs 2010 - 41
42. Grapes / Grab
// Google Collections example
@Grab('com.google.collections:google-collections:1.0')
import com.google.common.collect.HashBiMap
HashBiMap fruit =
[grape:'purple', lemon:'yellow', lime:'green']
assert fruit.lemon == 'yellow'
© ASERT 2006-2010
assert fruit.inverse().yellow == 'lemon'
DSLs 2010 - 42
43. Better Design Patterns: Immutable...
• Java Immutable Class
– As per Joshua Bloch // ...
@Override
Effective Java public boolean equals(Object obj) {
if (this == obj)
public final class Punter { return true;
private final String first; if (obj == null)
private final String last; return false;
if (getClass() != obj.getClass())
public String getFirst() { return false;
return first; Punter other = (Punter) obj;
} if (first == null) {
if (other.first != null)
© ASERT 2006-2010
public String getLast() { return false;
return last; } else if (!first.equals(other.first))
} return false;
if (last == null) {
@Override if (other.last != null)
public int hashCode() { return false;
final int prime = 31; } else if (!last.equals(other.last))
int result = 1; return false;
result = prime * result + ((first == null) return true;
? 0 : first.hashCode()); }
result = prime * result + ((last == null)
? 0 : last.hashCode()); @Override
return result; public String toString() {
} return "Punter(first:" + first
+ ", last:" + last + ")";
public Punter(String first, String last) { }
this.first = first;
this.last = last; }
}
// ...
DSLs 2010 - 43
44. ...Better Design Patterns: Immutable...
• Java Immutable Class boilerplate
– As per Joshua Bloch // ...
@Override
Effective Java public boolean equals(Object obj) {
if (this == obj)
public final class Punter { return true;
private final String first; if (obj == null)
private final String last; return false;
if (getClass() != obj.getClass())
public String getFirst() { return false;
return first; Punter other = (Punter) obj;
} if (first == null) {
if (other.first != null)
© ASERT 2006-2010
public String getLast() { return false;
return last; } else if (!first.equals(other.first))
} return false;
if (last == null) {
@Override if (other.last != null)
public int hashCode() { return false;
final int prime = 31; } else if (!last.equals(other.last))
int result = 1; return false;
result = prime * result + ((first == null) return true;
? 0 : first.hashCode()); }
result = prime * result + ((last == null)
? 0 : last.hashCode()); @Override
return result; public String toString() {
} return "Punter(first:" + first
+ ", last:" + last + ")";
public Punter(String first, String last) { }
this.first = first;
this.last = last; }
}
// ...
DSLs 2010 - 44
46. Malleable Syntax
order to buy 200.shares of GOOG {
limitPrice 500
allOrNone false
at the value of { qty * unitPrice - 100 }
}
take 2.pills of chloroquinine after 6.hours
© ASERT 2006-2010
def "length of Spock's & his friends' names"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
Groovy 1.8+ DSLs 2010 - 46
47. Topics
• DSL origins
• Groovy Intro
Groovy DSL Features
• Groovy DSL Examples
• Advanced Topics & Guidelines
© ASERT 2006-2010
• More Info
DSLs 2010 - 47
49. Import / Import Static
• Imports
@Grab('com.google.collections:google-collections:1.0')
import com.google.common.collect.HashBiMap as HashMap
def m = new HashMap()
m.key = 'value'
assert m.inverse().value == 'key'
© ASERT 2006-2010
• Static Imports
import static java.util.Calendar.getInstance as now
println now().format('yyyy/MMM/dd')
What Java gives us plus aliases
DSLs 2010 - 49
50. Static Imports...
Java Static Imports
Discussed later:
Builders
Closures
import groovy.swing.SwingXBuilder
© ASERT 2006-2010
import static java.awt.Color.*
import static java.lang.Math.*
def swing = new SwingXBuilder()
def frame = swing.frame(size: [300, 300]) {
graph(plots: [
[GREEN, {value -> sin(value)}],
[BLUE, {value -> cos(value)}],
[RED, {value -> tan(value)}]
])
}.show()
DSLs 2010 - 50
51. ...Static Imports...
Java gives us this
import groovy.swing.SwingXBuilder
© ASERT 2006-2010
import static java.awt.Color.*
import static java.lang.Math.*
def swing = new SwingXBuilder()
def frame = swing.frame(size: [300, 300]) {
graph(plots: [
[GREEN, {value -> sin(value)}],
[BLUE, {value -> cos(value)}],
[RED, {value -> tan(value)}]
])
}.show()
DSLs 2010 - 51
52. ...Static Imports...
Java gives us this
import groovy.swing.SwingXBuilder
© ASERT 2006-2010
import static java.awt.Color.*
import static java.lang.Math.*
def swing = new SwingXBuilder()
def frame = swing.frame(size: [300, 300]) {
graph(plots: [
[GREEN, {value -> sin(value)}],
[BLUE, {value -> cos(value)}],
[RED, {value -> tan(value)}]
])
}.show()
DSLs 2010 - 52
54. ...Static Imports
Java doesn‟t give us this!
import groovy.swing.SwingXBuilder
import static java.lang.Math.*
import static java.awt.Color.GREEN as Lime
© ASERT 2006-2010
import static java.awt.Color.BLUE as Sky
import static java.awt.Color.RED as Maraschino
def swing = new SwingXBuilder()
def frame = swing.frame(size: [300, 300]) {
graph(plots: [
[Lime, {value -> sin(value)}],
[Sky, {value -> cos(value)}],
[Maraschino, {value -> tan(value)}]
])
}.show()
DSLs 2010 - 54
55. Literal Syntax Conventions
• Lists
– Special syntax for list literals
– Additional common methods (operator overloading)
def list = [3, new Date(), 'Jan']
assert list + list == list * 2
• Maps
© ASERT 2006-2010
– Special syntax for map literals
– Additional common methods
def map = [a: 1, b: 2]
assert map['a'] == 1 && map.b == 2
• Ranges
– Special syntax for various kinds of ranges
def letters = 'a'..'z'
def numbers = 0..<10
DSLs 2010 - 55
56. Literal Syntax Conventions in DSLs
import static java.util.Calendar.getInstance as getNow
import static java.util.Calendar.*
def discount = [normal:0, silver:5, gold:10]
def roomrate = [weekday:150, weekend:95]
for (level in ['normal', 'silver', 'gold']) {
def multiplier = 1 - discount[level] / 100
print 'Your room rate is: '
© ASERT 2006-2010
if (now[DAY_OF_WEEK] in MONDAY..FRIDAY)
println roomrate.weekday * multiplier
else
println roomrate.weekend * multiplier
}
Literal list & map syntax
Ranges
Your room rate is: 150 Also
Your room rate is: 142.50 Static import aliases
Your room rate is: 135.0 BigDecimal arithmetic
DSLs 2010 - 56
57. Compact Syntax...
• Java
public class BuySharesJava {
public static void buyShares(int qty, String name) {
// business logic here ...
System.out.println("buying " + qty
+ " shares of " + name);
}
© ASERT 2006-2010
public static void main(String[] args) {
buyShares(3, "BHP");
} Script syntax
Conventions for visibility
}
GDK methods: println
• Groovy Implicit typing
GString interpolation
def buyShares(qty, name) { Optional brackets & „;‟
// business logic here ...
println "buying $qty shares of $name"
}
buyShares 3, 'BHP'
DSLs 2010 - 57
58. ...Compact Syntax...
• Java Script syntax, Named params, Extra imports
Conventions for visibility, GDK methods
import java.util.Date; Implicit typing, Multi-line GString
import java.util.HashMap;
Elvis operator, Optional brackets & „;‟
import java.util.Map;
public class ProcessCustomerJava {
public static void printDetails(Map<String, String> cust) {
System.out.println("Details as at: " + new Date());
String first = cust.get("first");
System.out.println("First name: " + first);
© ASERT 2006-2010
String last = cust.get("last");
System.out.println("Last name: " + (last != null ? last : "unknown"));
}
public static void main(String[] args) {
Map<String, String> details = new HashMap<String, String>();
details.put("first", "John");
details.put("last", "Smith");
printDetails(details);
}
} def printDetails(cust) {
println """Details as at: ${new Date()}
First name: $cust.first
• Groovy Last name: ${cust.last ?: 'unknown'}"""
}
printDetails first: 'John', last: 'Smith'
DSLs 2010 - 58
59. ...Compact Syntax
• Java
import java.util.Date;
public class ProcessStrongCustomerJava {
public static void printDetails(CustomerJ cust) {
System.out.println("Details as at: " + new Date());
String first = cust.getFirst();
System.out.println("First name: " + first);
• Groovy
String last = cust.getLast();
System.out.println("Last name: " + (last != null ? last : "unknown"));
}
public static void main(String[] args) {
CustomerJ c = new CustomerJ();
class CustomerG { String first, last }
© ASERT 2006-2010
c.setFirst("John");
c.setLast("Smith"); def printDetails(cust) {
printDetails(c);
} println """Details as at: ${new Date()}
}
First name: $cust.first
class CustomerJ { Last name: ${cust.last ?: 'unknown'}"""
private String first;
private String last; }
public String getFirst() {
return first;
printDetails new CustomerG(first: 'John',
} last: 'Smith')
public void setFirst(String first) {
this.first = first;
} Plus:
Named params for constructors
public String getLast() {
return last;
JavaBean conventions
}
public void setLast(String last) {
this.last = last; Leverage Duck Typing
}
}
DSLs 2010 - 59
60. Using With Example
letters = ['a', 'b', 'c']
range = 'b'..'d'
letters.with {
add 'd' Just normal methods
for ArrayList here
remove 'a'
}
© ASERT 2006-2010
assert letters == range
map = [a:10, b:4, c:7]
map.with {
assert (a + b) / c == 2
}
DSLs 2010 - 60
61. Closures...
• Traditional mainstream languages
– Data can be stored in variables, passed around,
combined in structured ways to form more complex
data; code stays put where it is defined
• Languages supporting closures
– Data and code can be stored in variables, passed
© ASERT 2006-2010
around, combined in structured ways to form more
complex algorithms and data; functional coding style
doubleNum = { num -> num * 2 }
println doubleNum(3) // => 6
processThenPrint = { num, closure ->
num = closure(num); println "num is $num"
}
processThenPrint(3, doubleNum) // => num is 6
processThenPrint(10) { it / 2 } // => num is 5
DSLs 2010 - 61
62. ...Closures...
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
Name
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 62
63. ...Closures...
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
Code
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 63
64. ...Closures...
Parameter(s)
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
Default parameter
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 64
65. ...Closures...
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
Call closure
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
Alternative syntax
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 65
66. ...Closures...
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
Free variable
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 66
67. ...Closures...
Bound to environment/context when called
int myConst = 4
def multiplier = { number -> number * myConst }
assert multiplier(10) == 40
© ASERT 2006-2010
Closure other = { it + myConst }
assert other.call(10) == 14
def twice = { it * 2 } // anonymous functions
assert twice(10) == 20 // implemented as Closures
DSLs 2010 - 67
68. Closures in DSLs...
Nothing special here.
import static java.util.Calendar.* Just a list of maps.
def cart1 = [
[qty: 3, unitPrice: 50.0, gift: true,
name: 'Groovy in Action Book'],
[qty: 1, unitPrice: 40.0, gift: false,
name: 'Grails in Action eBook'],
[qty: 2, unitPrice: 25.0, gift: true,
© ASERT 2006-2010
name: 'Avatar DVD']
]
def cart2 = [
[qty: 1, unitPrice: 50.0, gift: true,
name: 'Groovy in Action Book'],
[qty: 2, unitPrice: 30.0, gift: false,
name: 'Avatar 3D DVD']
]
// ...
DSLs 2010 - 68
69. ... Closures in DSLs...
// ...
def noDiscount = { it.unitPrice * it.qty }
def tenPercentOffAll = { it.unitPrice * it.qty * 0.9 }
def tenPercentOffGifts = { it.with {
unitPrice * qty * (gift ? 0.9 : 1.0)
} }
def tenPercentOffOverForty = { it.with {
unitPrice * qty * (unitPrice > 40 ? 0.9 : 1.0)
© ASERT 2006-2010
} }
def tenPercentOffBooks = { it.with {
unitPrice * qty * (name.contains('Book') ? 0.9 : 1.0)
} }
def tenPercentOffDVDs = { it.with {
unitPrice * qty * (name.contains('DVD') ? 0.9 : 1.0)
} }
def buyThreeGetOneFree = { it.with {
unitPrice * (qty % 3 + 2 * qty.intdiv(3))
} }
// ...
Closure Name
DSLs 2010 - 69
70. ... Closures in DSLs...
// ...
def noDiscount = { it.unitPrice * it.qty }
def tenPercentOffAll = { it.unitPrice * it.qty * 0.9 }
def tenPercentOffGifts = { it.with {
unitPrice * qty * (gift ? 0.9 : 1.0)
} }
def tenPercentOffOverForty = { it.with {
unitPrice * qty * (unitPrice > 40 ? 0.9 : 1.0)
© ASERT 2006-2010
} }
def tenPercentOffBooks = { it.with {
unitPrice * qty * (name.contains('Book') ? 0.9 : 1.0)
} }
def tenPercentOffDVDs = { it.with {
unitPrice * qty * (name.contains('DVD') ? 0.9 : 1.0)
} }
def buyThreeGetOneFree = { it.with {
unitPrice * (qty % 3 + 2 * qty.intdiv(3))
} }
// ...
Closure Code
DSLs 2010 - 70
71. ... Closures in DSLs
// ...
def specials = [noDiscount, tenPercentOffAll,
tenPercentOffGifts, tenPercentOffOverForty,
1 240.00
tenPercentOffBooks, tenPercentOffDVDs,
buyThreeGetOneFree] 2 216.00
3 220.00
[ 4 225.00
SUNDAY..SATURDAY, 5 221.00
© ASERT 2006-2010
[cart1, cart2] 6 235.00
].combinations().each { day, cart ->
7 190.00
printf "%d %2.2fn", day,
cart.sum(specials[day-1]) 1 110.00
} 2 99.00
3 105.00
4 105.00
5 105.00
6 104.00
7 110.00
DSLs 2010 - 71
72. Operator Overloading Example
• Java
BigDecimal a = new BigDecimal(3.5d);
BigDecimal b = new BigDecimal(4.0d);
assert a.multiply(b).compareTo(new BigDecimal(14.0d)) == 0;
assert a.multiply(b).equals(new BigDecimal(14.0d).setScale(1));
• Groovy
© ASERT 2006-2010
def c = 3.5, d = 4.0
assert c * d == 14.0
DSLs 2010 - 72
73. Groovy Lab Example
// require GroovyLab
import static org.math.array.Matrix.*
import static org.math.plot.Plot.*
def A = rand(10,3) // random Matrix of 10 rows and 3 columns
def B = fill(10,3,1.0) // one Matrix of 10 rows and 3 columns
def C = A + B // support for matrix addition with "+" or "-"
def D = A - 2.0 // support for number addition with "+" or "-"
def E = A * B
© ASERT 2006-2010
// support for matrix multiplication or division
def F = rand(3,3)
def G = F**(-1) // support for matrix power (with integers only)
println A // display Matrix content
plot("A",A,"SCATTER") // plot Matrix values as ScatterPlot
def M = rand(5,5) + id(5) // Eigenvalues decomposition
println "M=n" + M
println "V=n" + V(M)
println "D=n" + D(M)
println "M~n" + (V(M) * D(M) * V(M)**(-1))
DSLs 2010 - 73
74. Better Control Structures: Switch Poker…
hand1 hand2
8C TS KC 9H 4S 7D 2S 5D 3S AC
suits = 'SHDC'
ranks = '23456789TJQKA'
suit = { String card -> suits.indexOf(card[1]) }
rank = { String card -> ranks.indexOf(card[0]) }
rankSizes = { List cards ->
cards.groupBy(rank).collect{ k, v -> v.size() }.sort() }
© ASERT 2006-2010
rankValues = { List cards ->
cards.collect{ rank(it) }.sort() }
// ...
println rankSizes(["7S", "7H", "2H", "7D", "AH"])
// => [1, 1, 3]
DSLs 2010 - 74
75. …Better Control Structures: Switch Poker…
// ...
flush = { List cards -> cards.groupBy(suit).size() == 1 }
straight = { def v = rankValues(it); v == v[0]..v[0]+4 }
straightFlush = { List cards -> straight(cards) && flush(cards)
© ASERT 2006-2010
}
fourOfAKind = { List cards -> rankSizes(cards) == [1, 4] }
fullHouse = { List cards -> rankSizes(cards) == [2, 3] }
threeOfAKind = { List cards -> rankSizes(cards) == [1, 1, 3] }
twoPair = { List cards -> rankSizes(cards) == [1, 2, 2] }
pair = { List cards -> rankSizes(cards) == [1, 1, 1, 2] }
// ...
DSLs 2010 - 75
76. … Better Control Structures: Switch Poker
// ...
def rankHand(List cards) {
switch (cards) {
case straightFlush : return 9
case fourOfAKind : return 8
case fullHouse : return 7
case flush : return 6
© ASERT 2006-2010
case straight : return 5
case threeOfAKind : return 4
case twoPair : return 3
case pair : return 2
default : return 1
}
}
// ...
DSLs 2010 - 76
78. Builders…
• MarkupBuilder
<html>
import groovy.xml.*
<head>
def page = new MarkupBuilder()
<title>Hello</title>
page.html {
</head>
head { title 'Hello' }
<body>
body {
© ASERT 2006-2010
<ul>
ul {
<li>world 1</li>
for (count in 1..5) {
<li>world 2</li>
li "world $count"
<li>world 3</li>
} } } }
<li>world 4</li>
<li>world 5</li>
</ul>
</body>
</html>
DSLs 2010 - 78
79. ...Builders…
• GraphicsBuilder
def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
star( cx: 50 + (index*110), cy: 50, or: 40, ir: 15,
borderColor: 'black', count: 2+index, fill: colors[index] )
star( cx: 50 + (index*110), cy: 140, or: 40, ir: 15,
borderColor: 'black‘, count: 7+index, fill: colors[index] )
}
© ASERT 2006-2010
DSLs 2010 - 79
80. …Builders
new AntBuilder().with {
echo(file:'Temp.java', '''
class Temp {
public static void main(String[] args) {
System.out.println("Hello");
}
}
''')
© ASERT 2006-2010
javac(srcdir:'.', includes:'Temp.java', fork:'true')
java(classpath:'.', classname:'Temp', fork:'true')
echo('Done')
}
// =>
// [javac] Compiling 1 source file
// [java] Hello
// [echo] Done
DSLs 2010 - 80
81. Metaprogramming
• Runtime Metaprogramming
– Categories including DGM
– invokeMethod, methodMissing, getProperty
– GroovyInterceptable
– ExpandoMetaClass
© ASERT 2006-2010
• Compile-time Metaprogramming
– AST Macros
– Local and Global flavors based around annotations
– Reduced runtime overhead
– Several built-in annotations:
• @Immutable
• @Delegate
• @Singleton
DSLs 2010 - 81
82. ExpandoMetaClass…
import static java.lang.Character.isUpperCase
String.metaClass.swapCase = {
delegate.collect { ch ->
isUpperCase(ch as char) ?
ch.toLowerCase() :
ch.toUpperCase()
}.join()
}
© ASERT 2006-2010
println new Date().toString().swapCase()
// => sUN nOV 09 17:30:06 est 2008
List.metaClass.sizeDoubled = {-> delegate.size() * 2 }
LinkedList list = []
list << 1
list << 2
assert 4 == list.sizeDoubled()
DSLs 2010 - 82
83. …ExpandoMetaClass
class Person {
String name
}
class MortgageLender {
def borrowMoney() {
"buy house"
}
© ASERT 2006-2010
}
def lender = new MortgageLender()
Person.metaClass.buyHouse = lender.&borrowMoney
def p = new Person()
assert "buy house" == p.buyHouse()
DSLs 2010 - 83
84. Adding your own control structures
• Thread enhancement ROCK!
. 25
import java.util.concurrent.locks.ReentrantLock . 33
import static System.currentTimeMillis as now . 34
def startTime = now() . 35
ReentrantLock.metaClass.withLock = { critical -> . 36
lock() .. 131
try { critical() } .. 134
finally { unlock() } .. 137
}
def lock = new ReentrantLock()
.. 138
def worker = { threadNum -> .. 139
4.times { count -> ... 232
lock.withLock { ... 234
print " " * threadNum ... 237
print "." * (count + 1) ... 238
println " ${now() - startTime}" ... 239
} .... 334
Thread.sleep 100 .... 336
}
}
.... 337
5.times { Thread.start worker.curry(it) } .... 338
println "ROCK!" .... 339
Source: http://chrisbroadfoot.id.au/articles/2008/08/06/groovy-threads DSLs 2010 - 84
86. ...EMC Neo4j DSL…
@GrabResolver(name= 'neo4j-public-repo', root= 'http://m2.neo4j.org')
@Grab('org.neo4j:neo4j-kernel:1.1.1')
import org.neo4j.kernel.EmbeddedGraphDatabase
import org.neo4j.graphdb.*
// an enum helper
enum MyRelationships implements RelationshipType { knows }
// some optional syntactic sugar using EMC DSL
Node.metaClass {
© ASERT 2006-2010
propertyMissing { String name, val ->
delegate.setProperty(name, val) }
propertyMissing { String name -> delegate.getProperty(name) }
methodMissing { String name, args ->
delegate.createRelationshipTo(args[0], MyRelationships."$name") }
}
Relationship.metaClass {
propertyMissing { String name, val ->
delegate.setProperty(name, val) }
propertyMissing { String name -> delegate.getProperty(name) }
}
// ... DSLs 2010 - 86
87. …EMC Neo4j DSL
// ...
def graphDb = new EmbeddedGraphDatabase("graphdb")
def tx = graphDb.beginTx()
def firstNode, secondNode, relationship
try {
firstNode = graphDb.createNode()
secondNode = graphDb.createNode()
relationship = firstNode.knows(secondNode)
firstNode.message = "Hello,"
secondNode.message = "world!"
© ASERT 2006-2010
relationship.message = "brave Neo4j"
tx.success()
} finally {
tx.finish()
println "$firstNode.message $relationship.message $secondNode.message"
// => Hello, brave Neo4j world!
graphDb.shutdown()
}
DSLs 2010 - 87
89. AST Builder
• Numerous approaches, still evolving.
“From code” approach:
def result = new AstBuilder().buildFromCode {
println "Hello World"
}
• Produces:
BlockStatement
-> ReturnStatement
-> MethodCallExpression
-> VariableExpression("this")
-> ConstantExpression("println")
-> ArgumentListExpression
-> ConstantExpression("Hello World")
DSLs 2010 - 89
90. Type Transformation Example
class InventoryItem {
def weight, name
InventoryItem(Map m) {
this.weight = m.weight; this.name = m.name
}
InventoryItem(weight, name) {
this.weight = weight; this.name = name
}
InventoryItem(String s) {
© ASERT 2006-2010
s.find(/weight=(d*)/) { all, w -> this.weight = w }
s.find(/name=(.*)/) { all, n -> this.name = n }
}
}
def room = [:]
def gold = [weight:50, name:'Gold'] as InventoryItem
def emerald = [10, 'Emerald'] as InventoryItem
def dagger = ['weight=5, name=Dagger'] as InventoryItem
room.contents = [gold, emerald, dagger]
room.contents.each{ println it.dump() }
DSLs 2010 - 90
91. GParsec...
• Miniature parsers
def isLetter = { ch -> return Character.isLetter(ch) }
def isDigit = { ch -> return Character.isDigit(ch) }
def letter = satisfyC(isLetter)
def digit = satisfyC(isDigit)
• Parser Combinators
def letterAndDigit = seqC(letter, digit)
assert letterAndDigit('a123###') == ['a', '1']
def identifier = seqC(identifierStart,
noneOrMoreC(identifierRest))
DSLs 2010 - 91
92. ...GParsec...
• BNF
variableDeclarator :
identifier ( '=' expression )?
variableDefinitions :
variableDeclarator ( ',' variableDeclarator)*
• Groovy definitions
def variableDeclarator =
seqC(identifier, optionalC(seqC(assign, expression)))
def variableDefinitions =
seqC(variableDeclarator,
noneOrMoreC(seqC(comma, variableDeclarator)))
DSLs 2010 - 92
93. ...GParsec
• satisfyC: combinator consumes a single input when its
predicate succeeds
• altC (alt3C, altCs): is the choice combinator. Given two
parsers it only looks at its second alternative if the first has
not consumed any input - regardless of the final value
• seqC (seq3C, seqCs): is the sequencing combinator. It runs
two parsers in succession and if successful, returns the result
of the two parsers
• noneOrMoreC, oneOrMoreC: applies a parser zero or more
times to an input stream. The result from each application of
the parser are returned in a list
• optionalC: The combinator optionalC may succeed in
parsing some input. It always returns success.
DSLs 2010 - 93
94. Using External Parsers
import static com.mdimension.jchronic.Chronic.parse
def span = parse("tomorrow at 5pm")
println span.endCalendar.format('yyyy-MM-dd HH:mm')
// => 2010-05-19 17:00
© ASERT 2006-2010
DSLs 2010 - 94
95. Topics
• DSL origins
• Groovy Intro
• Groovy DSL Features
Groovy DSL Examples
• Advanced Topics & Guidelines
© ASERT 2006-2010
• More Info
DSLs 2010 - 95
96. Coin example...
enum Coin {
penny(1), nickel(5), dime(10), quarter(25)
Coin(int value) { this.value = value }
int value
}
© ASERT 2006-2010
import static Coin.*
assert 2 * quarter.value +
1 * nickel.value +
2 * penny.value == 57
DSLs 2010 - 96
97. ...Coin example...
class CoinMath {
static multiply(Integer self, Coin c) {
self * c.value
}
}
use (CoinMath) {
© ASERT 2006-2010
assert 2 * quarter +
1 * nickel +
2 * penny == 57
}
// EMC equivalent
Integer.metaClass.multiply = {
Coin c -> delegate * c.value
}
assert 2 * quarter + 1 * nickel + 2 * penny == 57
DSLs 2010 - 97
98. ...Coin example
class CoinValues {
static get(Integer self, String name) {
self * Coin."${singular(name)}".value
}
static singular(String val) {
val.endsWith('ies') ? val[0..-4] + 'y' : val.endsWith('s') ? val[0..-2] : val
}
}
use (CoinValues) {
assert 2.quarters + 1.nickel + 2.pennies == 57
© ASERT 2006-2010
}
// EMC equivalent
Integer.metaClass.getProperty = { String name ->
def mp = Integer.metaClass.getMetaProperty(name)
if (mp) return mp.getProperty(delegate)
def singular = name.endsWith('ies') ? name[0..-4] + 'y' :
name.endsWith('s') ? name[0..-2] : name
delegate * Coin."$singular".value
}
assert 2.quarters + 1.nickel + 2.pennies == 57
DSLs 2010 - 98
99. Game example...
// Trying out the game DSL idea by Sten Anderson from:
// http://blogs.citytechinc.com/sanderson/?p=92
class GameUtils {
static VOWELS = ['a', 'e', 'i', 'o', 'u']
static listItems(things) {
def result = ''
things.eachWithIndex{ thing, index ->
if (index > 0) {
© ASERT 2006-2010
if (index == things.size() - 1) result += ' and '
else if (index < things.size() - 1) result += ', '
}
result += "${thing.toLowerCase()[0] in VOWELS ? 'an' : 'a'} $thing
}
result ?: 'nothing'
}
}
import static GameUtils.*
...
DSLs 2010 - 99
100. ...Game example...
...
class Room {
def description
def contents = []
}
...
© ASERT 2006-2010
DSLs 2010 - 100
101. ...Game example...
...
class Player {
def currentRoom
def inventory = []
void look() {
println "You are in ${currentRoom?.description?:'the void'}
which contains ${listItems(currentRoom?.contents)}"
}
© ASERT 2006-2010
void inv() {
println "You are holding ${listItems(inventory)}"
}
void take(item) {
if (currentRoom?.contents?.remove(item)) {
inventory << item
println "You took the $item"
} else {
println "I see no $item here"
}
}
... DSLs 2010 - 101
102. ...Game example...
...
void drop(item) {
if (inventory?.remove(item)) {
currentRoom?.contents << item
println "You dropped the $item"
} else {
println "You don't have the $item"
}
© ASERT 2006-2010
}
def propertyMissing(String name) {
if (metaClass.respondsTo(this, name)) {
this."$name"()
}
name
}
}
...
DSLs 2010 - 102
103. ...Game example...
...
Room plainRoom = new Room(description:'a plain white room',
contents:['dagger', 'emerald', 'key'])
Player player = new Player(currentRoom:plainRoom)
player.with {
inv
look
take dagger
© ASERT 2006-2010
inv
look
take emerald
inv
look
take key
drop emerald
inv
look
}
assert player.inventory == ['dagger', 'key']
... DSLs 2010 - 103
104. ...Game example
...
// now try some error conditions
plainRoom.description = null
player.with {
drop gold
take gold
drop emerald
© ASERT 2006-2010
take emerald
take emerald
look
}
DSLs 2010 - 104
105. GEP3 example...
Object.metaClass.please =
{ clos -> clos(delegate) }
Object.metaClass.the =
{ clos -> delegate[1](clos(delegate[0])) }
show = { thing -> [thing, { println it }] }
square_root = { Math.sqrt(it) }
© ASERT 2006-2010
given = { it }
given 100 please show the square_root
// ==> 10.0
DSLs 2010 - 105
106. ...GEP3 example...
Object.metaClass.of =
{ delegate[0](delegate[1](it)) }
Object.metaClass.the =
{ clos -> [delegate[0], clos] }
show = [{ println it }]
© ASERT 2006-2010
square_root = { Math.sqrt(it) }
please = { it }
please show the square_root of 100
// ==> 10.0
DSLs 2010 - 106
107. ...GEP3 example...
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
© ASERT 2006-2010
}]
}
please show the square_root of 100
// ==> 10.0
Inspiration for this example came from …
DSLs 2010 - 107
108. ...GEP3 example...
// Japanese DSL using GEP3 rules
Object.metaClass.を =
Object.metaClass.の =
{ clos -> clos(delegate) }
まず = { it }
表示する = { println it }
© ASERT 2006-2010
平方根 = { Math.sqrt(it) }
まず 100 の 平方根 を 表示する
// First, show the square root of 100
// => 10.0
// source: http://d.hatena.ne.jp/uehaj/20100919/1284906117
// http://groovyconsole.appspot.com/edit/241001
DSLs 2010 - 108
109. ...GEP3 example
© ASERT 2006-2010
// source: http://d.hatena.ne.jp/uehaj/20100919/1284906117
// http://groovyconsole.appspot.com/edit/241001
DSLs 2010 - 109