SlideShare a Scribd company logo
1 of 165
Download to read offline
Writing Domain Specific
© ASERT 2006-2010




                    Languages (DSLs) using Groovy
                         Dr Paul King, @paulk_asert
                           paulk at asert.com.au
Topics
                    DSL Origins
                    • Groovy Intro
                    • Groovy DSL Features
                    • Groovy DSL Examples
                    • Advanced Topics & Guidelines
© ASERT 2006-2010




                    • More Info




                                                     DSLs 2010 - 2
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
...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
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
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
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
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
...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
...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
And now? The twitterverse says...




                                    DSLs 2010 - 11
...And Now? The twitterverse says




                                    DSLs 2010 - 12
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
"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
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
...DSL usage patterns...
© ASERT 2006-2010




          Source: DSLs in Action, Debasish Ghosh, Manning now in MEAP   DSLs 2010 - 16
...DSL usage patterns
© ASERT 2006-2010




          Source: DSLs in Action, Debasish Ghosh, Manning now in MEAP   DSLs 2010 - 17
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.
Topics
                    • DSL origins
                    Groovy Intro
                    • Groovy DSL Features
                    • Groovy DSL Examples
                    • Advanced Topics & Guidelines
© ASERT 2006-2010




                    • More Info




                                                     DSLs 2010 - 19
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
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
Growing Acceptance …
  A slow and steady start but now gaining in
  momentum, maturity and mindshare




Now free
… Growing Acceptance …
© ASERT 2006-2010




                                             DSLs 2010 - 23
… Growing Acceptance …
© ASERT 2006-2010




                    Groovy and Grails downloads:
                    70-90K per month and growing   DSLs 2010 - 24
… Growing Acceptance …
© ASERT 2006-2010




                             Source: http://www.micropoll.com/akira/mpresult/501697-116746




                                                   Source: http://www.grailspodcast.com/
                                                                                        DSLs 2010 - 25
… Growing Acceptance …
© ASERT 2006-2010




                          http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes




                                                              http://www.java.net
                                                                                       DSLs 2010 - 26
… Growing Acceptance …
         What alternative JVM language are you using or intending to use
© ASERT 2006-2010




                           http://www.leonardoborges.com/writings
                                                                    DSLs 2010 - 27
… Growing Acceptance …
© ASERT 2006-2010




                    http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com)
                                                                                                                  DSLs 2010 - 28
… Growing Acceptance
© ASERT 2006-2010




                                           DSLs 2010 - 29
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
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
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
...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
...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
...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
...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
...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
...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
...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
...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
...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
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
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
...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
...Better Design Patterns: Immutable



                          @Immutable class Punter {
                              String first, last
                          }
© ASERT 2006-2010




                                                      DSLs 2010 - 45
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
Topics
                    • DSL origins
                    • Groovy Intro
                    Groovy DSL Features
                    • Groovy DSL Examples
                    • Advanced Topics & Guidelines
© ASERT 2006-2010




                    • More Info




                                                     DSLs 2010 - 47
Groovy DSL Features
© ASERT 2006-2010




                                          DSLs 2010 - 48
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
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
...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
...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
...Static Imports...
© ASERT 2006-2010




      Source: http://www.thedoghousediaries.com/?p=1406        DSLs 2010 - 53
...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
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
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
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
...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
...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
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
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
...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
...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
...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
...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
...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
...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
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
... 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
... 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
... 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
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
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
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
…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
… 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
Groovy DSL Features
© ASERT 2006-2010




                                          DSLs 2010 - 77
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
...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
…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
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
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
…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
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
EMC DSL…
© ASERT 2006-2010




                               DSLs 2010 - 85
...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
…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
Groovy DSL Features
© ASERT 2006-2010




                                          DSLs 2010 - 88
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
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
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
...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
...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
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
Topics
                    • DSL origins
                    • Groovy Intro
                    • Groovy DSL Features
                    Groovy DSL Examples
                    • Advanced Topics & Guidelines
© ASERT 2006-2010




                    • More Info




                                                     DSLs 2010 - 95
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
...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
...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
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
...Game example...
                    ...
                    class Room {
                      def description
                      def contents = []
                    }
                    ...
© ASERT 2006-2010




                                                           DSLs 2010 - 100
...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
...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
...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
...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
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
...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
...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
...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
...GEP3 example
© ASERT 2006-2010




                    // source: http://d.hatena.ne.jp/uehaj/20100919/1284906117
                    // http://groovyconsole.appspot.com/edit/241001

                                                                                 DSLs 2010 - 109
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs
GroovyDSLs

More Related Content

Similar to GroovyDSLs

groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
Bdd and dsl как способ построения коммуникации на проекте
Bdd and dsl как способ построения коммуникации на проектеBdd and dsl как способ построения коммуникации на проекте
Bdd and dsl как способ построения коммуникации на проектеISsoft
 
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...SQALab
 
Oopsla 2008 Panel Ds Ls The Good The Bad And The Ugly
Oopsla 2008 Panel Ds Ls The Good The Bad And The UglyOopsla 2008 Panel Ds Ls The Good The Bad And The Ugly
Oopsla 2008 Panel Ds Ls The Good The Bad And The UglyOOPSLA2008
 
Domain Specific Languages
Domain Specific LanguagesDomain Specific Languages
Domain Specific LanguagesLakshan Perera
 
DSL Construction rith Ruby
DSL Construction rith RubyDSL Construction rith Ruby
DSL Construction rith RubyThoughtWorks
 
Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language FeatureRob Tweed
 
On the Use of an Internal DSL for Enriching EMF Models
On the Use of an Internal DSL for Enriching EMF ModelsOn the Use of an Internal DSL for Enriching EMF Models
On the Use of an Internal DSL for Enriching EMF ModelsFilip Krikava
 
CH # 1 preliminaries
CH # 1 preliminariesCH # 1 preliminaries
CH # 1 preliminariesMunawar Ahmed
 
Variability Management in Domain Specific Languages
Variability Management in Domain Specific LanguagesVariability Management in Domain Specific Languages
Variability Management in Domain Specific LanguagesDavid Méndez-Acuña
 
Salyens Smeet SDK ™ H.323
Salyens Smeet SDK ™ H.323 Salyens Smeet SDK ™ H.323
Salyens Smeet SDK ™ H.323 Videoguy
 
Creating a textual domain specific language
Creating a textual domain specific languageCreating a textual domain specific language
Creating a textual domain specific languageVicente García Díaz
 
Safe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereSafe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereAdaCore
 
Standardizing the Data Distribution Service (DDS) API for Modern C++
Standardizing the Data Distribution Service (DDS) API for Modern C++Standardizing the Data Distribution Service (DDS) API for Modern C++
Standardizing the Data Distribution Service (DDS) API for Modern C++Sumant Tambe
 

Similar to GroovyDSLs (20)

groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
Bdd and dsl как способ построения коммуникации на проекте
Bdd and dsl как способ построения коммуникации на проектеBdd and dsl как способ построения коммуникации на проекте
Bdd and dsl как способ построения коммуникации на проекте
 
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
 
Oopsla 2008 Panel Ds Ls The Good The Bad And The Ugly
Oopsla 2008 Panel Ds Ls The Good The Bad And The UglyOopsla 2008 Panel Ds Ls The Good The Bad And The Ugly
Oopsla 2008 Panel Ds Ls The Good The Bad And The Ugly
 
Metamorphic Domain-Specific Languages
Metamorphic Domain-Specific LanguagesMetamorphic Domain-Specific Languages
Metamorphic Domain-Specific Languages
 
Programming landuages
Programming landuagesProgramming landuages
Programming landuages
 
Domain Specific Languages
Domain Specific LanguagesDomain Specific Languages
Domain Specific Languages
 
Groovy DSL
Groovy DSLGroovy DSL
Groovy DSL
 
DSL Construction rith Ruby
DSL Construction rith RubyDSL Construction rith Ruby
DSL Construction rith Ruby
 
Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language Feature
 
On the Use of an Internal DSL for Enriching EMF Models
On the Use of an Internal DSL for Enriching EMF ModelsOn the Use of an Internal DSL for Enriching EMF Models
On the Use of an Internal DSL for Enriching EMF Models
 
Antlr Conexaojava
Antlr ConexaojavaAntlr Conexaojava
Antlr Conexaojava
 
CH # 1 preliminaries
CH # 1 preliminariesCH # 1 preliminaries
CH # 1 preliminaries
 
Variability Management in Domain Specific Languages
Variability Management in Domain Specific LanguagesVariability Management in Domain Specific Languages
Variability Management in Domain Specific Languages
 
An Introduction to ADA.pptx
An Introduction to ADA.pptxAn Introduction to ADA.pptx
An Introduction to ADA.pptx
 
Salyens Smeet SDK ™ H.323
Salyens Smeet SDK ™ H.323 Salyens Smeet SDK ™ H.323
Salyens Smeet SDK ™ H.323
 
Creating a textual domain specific language
Creating a textual domain specific languageCreating a textual domain specific language
Creating a textual domain specific language
 
Scam 08
Scam 08Scam 08
Scam 08
 
Safe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereSafe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get There
 
Standardizing the Data Distribution Service (DDS) API for Modern C++
Standardizing the Data Distribution Service (DDS) API for Modern C++Standardizing the Data Distribution Service (DDS) API for Modern C++
Standardizing the Data Distribution Service (DDS) API for Modern C++
 

More from Paul King

awesome groovy
awesome groovyawesome groovy
awesome groovyPaul King
 
groovy databases
groovy databasesgroovy databases
groovy databasesPaul King
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gparsPaul King
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovyPaul King
 
groovy rules
groovy rulesgroovy rules
groovy rulesPaul King
 
functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GParsPaul King
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

More from Paul King (19)

awesome groovy
awesome groovyawesome groovy
awesome groovy
 
groovy databases
groovy databasesgroovy databases
groovy databases
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovy
 
groovy rules
groovy rulesgroovy rules
groovy rules
 
functional groovy
functional groovyfunctional groovy
functional groovy
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GPars
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Recently uploaded

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfSeasiaInfotech2
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 

Recently uploaded (20)

WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdf
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 

GroovyDSLs

  • 1. Writing Domain Specific © ASERT 2006-2010 Languages (DSLs) using Groovy Dr Paul King, @paulk_asert paulk at asert.com.au
  • 2. 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
  • 11. And now? The twitterverse says... DSLs 2010 - 11
  • 12. ...And Now? The twitterverse says DSLs 2010 - 12
  • 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
  • 16. ...DSL usage patterns... © ASERT 2006-2010 Source: DSLs in Action, Debasish Ghosh, Manning now in MEAP DSLs 2010 - 16
  • 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
  • 23. … Growing Acceptance … © ASERT 2006-2010 DSLs 2010 - 23
  • 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
  • 29. … Growing Acceptance © ASERT 2006-2010 DSLs 2010 - 29
  • 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
  • 45. ...Better Design Patterns: Immutable @Immutable class Punter { String first, last } © ASERT 2006-2010 DSLs 2010 - 45
  • 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
  • 48. Groovy DSL Features © ASERT 2006-2010 DSLs 2010 - 48
  • 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
  • 53. ...Static Imports... © ASERT 2006-2010 Source: http://www.thedoghousediaries.com/?p=1406 DSLs 2010 - 53
  • 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
  • 77. Groovy DSL Features © ASERT 2006-2010 DSLs 2010 - 77
  • 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
  • 85. EMC DSL… © ASERT 2006-2010 DSLs 2010 - 85
  • 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
  • 88. Groovy DSL Features © ASERT 2006-2010 DSLs 2010 - 88
  • 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