groovy e domain specific languages
TRANSCRIPT
![Page 1: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/1.jpg)
GROOVY E DSLBY TIZIANO LATTISI
![Page 2: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/2.jpg)
INDICE
• cos’è Groovy?
• caratteristiche interessanti (a mio giudizio), in ordine arbitrariamente sparso
• cos’è un DSL?
• caratteristiche che rendono Groovy adatto a costruire un DSL
• un esempio semplice (lo facciamo al volo)
• un esempio meno semplice (non lo facciamo al volo)
nota: in parallelo vedremo esempi
![Page 3: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/3.jpg)
COS’È GROOVY (PARTE 1)?
• linguaggio per la JVM alternativo a Java
• ispirato a: Ruby, Python, Smalltalk
• consente compilazione dinamica, ma può anche generare bytecode
• tipizzazione forte dinamica
• closure
![Page 4: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/4.jpg)
COMMAND-LINE GROOVY HELLO GROOVY
• $ groovy -e “println ‘Hello Groovy’”!
• $ echo “println ‘Hello Groovy’” > hello.groovy $ groovy hello.groovy!
• $ groovy -Dmsg=Groovy -e “println ‘Hello ‘ + System.getProperty(‘msg’)”
![Page 5: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/5.jpg)
COMMAND-LINE GROOVY SWITCH -N -P -I
• $ printf “1\n2” > data.txt $ groovy -n -e “println line.toLong()*2” data.txt 2 4!
• $ groovy -i.bak —n -p -e “line.toLong()*2” data.txt $ cat data.txt 2 4 $ cat data.txt.bak 1 2
![Page 6: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/6.jpg)
COMMAND-LINE GROOVY SWITCH -L
• $ groovy -l 1234 -e “if(line==‘DATE’){println new Date()}”Groovy is listening on port 1234 $ telnet localhost 1234 Connect to localhost. Escape character is ‘^]’. DATE Mon Mar 17 21:38:49 CET 2014!
• $ groovy -l 80 SimpleWebServer.groovy Esempio nei sorgenti Groovy: serve i file di una cartella come webserver
![Page 7: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/7.jpg)
COS’È GROOVY (PARTE 2)?• è possibile usare direttamente l’API Java
• accetta (salvo alcune eccezioni) anche sintassi Java (es. {1,2,3,4} non è array, ma [1,2,3,4] è un ArrayList)
• import automatico (di convenienza) di alcune classi Java (es. java.io, java.lang, java.net, java.util…)
• println -> System.out.println
• parentesi opzionali nella chiamata a metodi
• notazione breve per getter e setter (o.field -> o.getField())
• ; opzionale (salvo alcune eccezioni)
• return opzionale (viene restituita l’ultima valutazione)
• this usata in contesti statici punta alla classe
![Page 8: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/8.jpg)
COS’È GROOVY (PARTE 3)?• GString interpolation: “Hello ${name}”
• Lazily interpolation (eval in conversione a String): “Nr. ${-> i}”
• == -> equals, equals -> is: non più “a != null && a.equals()” !!
• in: è un operatore basato su contains(): 3 in [1,2,3,4]
• sintassi nativa per alcune strutture dati: [1,2,3] list, [TN:’Trento’, BZ:’Bolzano] map, 1..10 range
• contesti booleani: if( myString!=null && myString.length>0 ){} -> if(myString){}
• safe-dereferencing: email?.destinatario?.indirizzo
• costrutto and-or (Elvis operator): def result = name != null ? name : “Unknow”
![Page 9: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/9.jpg)
FOR E FOR EACH
• for (int i=0; i<n; i++) { … }!
• for (i in 0..n-1) { … }!
• for (i in 0..<n) { … }!
• n.times { … }
• (1..5).each { println “nr. $it” }!
• 1..5 instanceof List!
• { println “nr. $it” }(3) nr. 3
![Page 10: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/10.jpg)
GROOVY BEANS
class Book { String title String description } def b1 = new Book() b1.setTitle(“Anna Karenina”) b1.description = “A very long book…”!
println b2.getDescription() println b2.title def b2 = new Book(title:”Anna Karenina”) println b2.title
![Page 11: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/11.jpg)
ALTRO SUI GROOVY BEANS• Annotation based AST transformation (groovy.transform.*):!
• @Immutable (read only bean)!
• @ToString(includeNames=true, excludes=‘description,year’)!
• @EqualsAndHashCode!
• @Canonical (@ToString + @EqualsAndHashCode)!
• @TupleConstructor -> new Book(“Anna Karenina”, “Very long book…”)!
• @AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR) def book1 = new Book(title:”Anna Karenina”) def book2 = book1.clone() assert book1.title == book2.title!
• AutoCloneStyle.SERIALIZABLE se implementa Serializable
![Page 12: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/12.jpg)
ALTRE COSE BELLE
• methodMissing e propertyMissing per gestire accessi a proprietà o metodi mancanti
• supporto nativo per markup XML, Json (Slurper e Builder)
• conversioni bean in xml e json
• ExpandoMetaClass
• built-in memoize
![Page 13: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/13.jpg)
DSL DOMAIN SPECIFIC LANGUAGE
• Linguaggio di programmazione dedicato ad uno specifico dominio (contrapposto a “general-purpose”)
• statistica (R e S)
• programmazione matriciale (Mata)
• Logo
• SQL
• …
![Page 14: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/14.jpg)
DEFINIRE UN DSL IN GROOVY
Combinazione di tre punti chiave:
• fluent API
• embedded shell
• “specificità” del linguaggio
![Page 15: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/15.jpg)
FLUENT API
È un implementazione di un API “method chaining”, ovvero che permette chiamate di metodi “a catena”.
Es. JavaFX
Scene scene = SceneBuilder.create().width(516).height(387) ! .root(! ! GroupBuilder.create().children(! ! ! ImageViewBuilder.create().image(new Image(“..”)),! ! ! [omissam]! ! ).build()).build();
![Page 16: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/16.jpg)
EMBEDING GROOVY IN JAVA// Semplice esecuzione di codice Groovy!
GroovyShell shell = new GroovyShell(); String groovyCode = “println ‘Hello ’ + ‘Groovy’”; String out = shell.evaluate(groovyCode);
// Esecuzione codice Groovy con variabile embeddata!
Book myBook = new Book(); myBook.setTitle(“Anna Karenina”);!
Binding binding = new Binding(); binding.setVariable(“book”, myBook); String groovyCode = “println ‘Reading ‘ + book.title”; GroovyShell shell = new GroovyShell(binding); String out = shell.evaluate(groovyCode);
![Page 17: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/17.jpg)
SPECIFICITÀ DI GROOVY
• le chiamate di metodi possono omettere le parentesi
• scriptBaseClass: la classe script base che rappresenta il contesto di esecuzione dello script
• ImportCustomizer: importazione diretta nello script di classi e package (anche *)
• SecureASTCustomizer: gestione della sicurezza (es. liste bianche/nere)
• ASTTransformationCustomizer: per aggiungere automaticamente annotazioni di trasformazione ai metodi
![Page 18: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/18.jpg)
ESEMPIO SEMPLICEVoglio creare un DSL in grado di eseguire:
compute 4 plus 3 plus 2 minus 1 print total!
• Una classe che implementa i metodi di linguaggio (compute, plus, minus, etc) come API fluent
• Una classe astratta come base dello script groovy (si occuperà di proxare i metodi sullo script)
• Un enum per le costanti (es. total)
• GroovyShell con:
• binding della classe linguaggio
• classe base astratta (vedi sopra)
• importazione custom delle costanti (vedi sopra)
Vediamo in pratica come procedere! (sorry, no slides here…)
![Page 19: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/19.jpg)
LA CLASSE DI LINGUAGGIOclass Language { Integer tot; def compute(Integer a){ tot=a this } def plus(Integer a){ tot += a this } def minus(Integer a){ tot -= a this } def print(Consts c){ tot } }
![Page 20: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/20.jpg)
LA SCRIPTBASE E LA COSTANTEabstract class AbstractScriptBaseClass extends Script{ def compute(Integer a){ this.lang.compute(a) } def plus(Integer a){ this.lang.plus(a) } def minus(Integer a){ this.lang.minus(a) } def print(String t){ this.lang.print(t) } }
public enum Consts { total }
![Page 21: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/21.jpg)
TESTIAMO L’API
void testAPI() { Language lang = new Language() String total = "total" // test API con sintassi tradizionale Integer tot1 = lang.compute(4).plus(3).plus(2).minus(1).print(total) // test API omettendo le parentesi Integer tot2 = lang.compute 4 plus 3 plus 2 minus 1 print total assert tot1 == 8 assert tot2 == 8 }
![Page 22: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/22.jpg)
TESTIAMO IL DSLvoid testShell() { // il codice scritto nel DSL def code = "compute 4 plus 3 plus 2 minus 1 print total" Language lang = new Language() Binding binding = new Binding(); binding.setVariable("lang", lang) CompilerConfiguration conf = new CompilerConfiguration() conf.scriptBaseClass = AbstractScriptBaseClass.class.name ImportCustomizer imports = new ImportCustomizer() imports.addStaticStars(Consts.name) conf.addCompilationCustomizers(imports) GroovyShell shell = new GroovyShell(binding, conf) Integer tot = (Integer) shell.evaluate(code) assert tot == 8 }
![Page 23: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/23.jpg)
ESEMPIO MENO SEMPLICEUn DSL per definire e risolvere problemi di geometria piana euclidea(https://github.com/tizianolattisi/peg)
create triangle name "ABC" extend "AC" to "D" with measure:"BC" extend "BC" to "E" with measure:"AC" create segment name "ED" extend "DE" to "H" extend "AB" to "H" apply "10.8" on "ad", "bc" //angoli opposti apply "10.3" on "CED", "ABC" apply "10.6" on "ABC", "cba", "CED", "edc" create segment name "BD" apply "10.10" on "BCD", "BC", "CD"
![Page 24: Groovy e Domain Specific Languages](https://reader033.vdocumenti.com/reader033/viewer/2022052400/55a2d5af1a28abea278b487b/html5/thumbnails/24.jpg)
–Tiziano Lattisi
“Grazie a tutti!”.