04 2013 2014 Comunicazione 1 -> molti: DNS (Domain Name System) e nomi a dominio
Groovy e Domain Specific Languages
-
Upload
tiziano-lattisi -
Category
Technology
-
view
267 -
download
0
Transcript of Groovy e Domain Specific Languages
GROOVY E DSLBY TIZIANO LATTISI
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
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
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’)”
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
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
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
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”
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
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
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
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
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
• …
DEFINIRE UN DSL IN GROOVY
Combinazione di tre punti chiave:
• fluent API
• embedded shell
• “specificità” del linguaggio
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();
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);
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
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…)
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 } }
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 }
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 }
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 }
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"
–Tiziano Lattisi
“Grazie a tutti!”.