Groovy Metaprogramming for Dummies

28
Groovy Metaprogramming for Dummies Darren Cruse (not a professional speaker: please no heckling or throwing of food per Groovy User Group Regulation 103a subsection e) Monday, June 7, 2010

Transcript of Groovy Metaprogramming for Dummies

Page 1: Groovy Metaprogramming for Dummies

Groovy Metaprogramming

for Dummies

Darren Cruse

(not a professional speaker: please no heckling or throwing of food per Groovy User Group Regulation 103a subsection e)

Monday, June 7, 2010

Page 2: Groovy Metaprogramming for Dummies

Meta Stuff(about the meta talk)

•Who am I?Once too good for “scripting”

Former Perl Nut

Recent Javascript Graduate

Improving Groovy Skills

Monday, June 7, 2010

Page 3: Groovy Metaprogramming for Dummies

Meta Stuff(about the meta talk)

•What’s this talk about?metaprogramming = “programming the program”

metaprogramming = what makes groovy really groovy!

Monday, June 7, 2010

Page 4: Groovy Metaprogramming for Dummies

Meta Stuff(about the meta talk)

• Who’s this talk for?

Ideally: Someone who’s seen a bit of groovy and aspires to get to the next level

Someone still unconvinced groovy’s power and productivity is sufficiently greater than java’s to merit learning a language with such a silly name.

(and you advanced guys feel free to dive in and help me out!)

Monday, June 7, 2010

Page 5: Groovy Metaprogramming for Dummies

Builders = Metaprogramming Magic

def builder = new StreamingMarkupBuilder()builder.encoding = 'UTF-8'def books = builder.bind { mkp.xmlDeclaration() namespaces << [meta:'http://meta/book/info'] books(count: 3) { book(id: 1) { title lang:'en', 'Groovy in Action' meta.isbn '1-932394-84-2' } book(id: 2) { title lang:'en', 'Groovy Programming' meta.isbn '0123725070' } book(id: 3) { title 'Groovy & Grails' comment << 'Not yet available.' } book(id: 4) { mkp.yieldUnescaped '<title>Griffon Guide</title>' } }}

Monday, June 7, 2010

Page 6: Groovy Metaprogramming for Dummies

Builders = Metaprogramming Magic

<?xml version="1.0" encoding="UTF-8"?><books xmlns:meta="http://meta/book/info" count="3"> <book id="1"> <title lang="en">Groovy in Action</title> <meta:isbn>1-932394-84-2</meta:isbn> </book> <book id="2"> <title lang="en">Groovy Programming</title> <meta:isbn>0123725070</meta:isbn> </book> <book id="3"> <title>Groovy &amp; Grails</title> <!--Not yet available.--> </book> <book id="4"> <title>Griffon Guide</title> </book></books>

Monday, June 7, 2010

Page 7: Groovy Metaprogramming for Dummies

def builder = new StreamingMarkupBuilder()builder.encoding = 'UTF-8'def books = builder.bind { mkp.xmlDeclaration() namespaces << [meta:'http://meta/book/info'] books(count: 3) { book(id: 1) { title lang:'en', 'Groovy in Action' meta.isbn '1-932394-84-2' } book(id: 2) { title lang:'en', 'Groovy Programming' meta.isbn '0123725070' } book(id: 3) { title 'Groovy & Grails' comment << 'Not yet available.' } book(id: 4) { mkp.yieldUnescaped '<title>Griffon Guide</title>' } }}

println XmlUtil.serialize(books)

Builders = Metaprogramming Magic

<?xml version="1.0" encoding="UTF-8"?><books xmlns:meta="http://meta/book/info" count="3"> <book id="1"> <title lang="en">Groovy in Action</title> <meta:isbn>1-932394-84-2</meta:isbn> </book> <book id="2"> <title lang="en">Groovy Programming</title> <meta:isbn>0123725070</meta:isbn> </book> <book id="3"> <title>Groovy &amp; Grails</title> <!--Not yet available.--> </book> <book id="4"> <title>Griffon Guide</title> </book></books>

Monday, June 7, 2010

Page 8: Groovy Metaprogramming for Dummies

book(id: 2) { title lang:'en', 'Groovy Programming' meta.isbn '0123725070'}

XML Builders = An alternative Syntax for XML

<book id="2"> <title lang="en">Groovy Programming</title> <meta:isbn>0123725070</meta:isbn></book>

Monday, June 7, 2010

Page 9: Groovy Metaprogramming for Dummies

But How?(is it code or is it data?)

Any sufficiently advanced technology is indistinguishable from magic.

Arthur C. Clarke,English physicist & science fiction author (1917 - )

def builder = new StreamingMarkupBuilder()builder.encoding = 'UTF-8'def books = builder.bind { mkp.xmlDeclaration() namespaces << [meta:'http://meta/book/info'] books(count: 3) { book(id: 1) { title lang:'en', 'Groovy in Action' meta.isbn '1-932394-84-2' } book(id: 2) { title lang:'en', 'Groovy Programming' meta.isbn '0123725070' } book(id: 3) { title 'Groovy & Grails' // & is converted to &amp; comment << 'Not yet available.' } book(id: 4) { mkp.yieldUnescaped '<title>Griffon Guide</title>' } }}

println XmlUtil.serialize(books)

Monday, June 7, 2010

Page 10: Groovy Metaprogramming for Dummies

The Answer Lies in Groovy’s Nature As a Dynamic Language

(as distinguished from java)

compile time

Java

run time

compile time

Groovy

run time

Monday, June 7, 2010

Page 11: Groovy Metaprogramming for Dummies

Java Compilation

javasource

bytecode

Monday, June 7, 2010

Page 12: Groovy Metaprogramming for Dummies

Groovy Compilation

groovysource

bytecode

metaprogram

Monday, June 7, 2010

Page 13: Groovy Metaprogramming for Dummies

The Program Generated...

Evaluates Your Program At Runtime...

...it does so using a framework that is designed for you to plug into...

When you do so you are “programming the program”...

i.e. metaprogramming.

Monday, June 7, 2010

Page 14: Groovy Metaprogramming for Dummies

This framework is called the “Meta Object Protocol”

(“protocol” in the sense of an “api”)

MOPMonday, June 7, 2010

Page 15: Groovy Metaprogramming for Dummies

If you’re familiar with a framework like Struts...

This really isn’t that different:

Instead of mapping url requests to the code that handles them, you’re mapping actual method calls and property accesses to the code that handles them!

Instead of using struts-config.xml to do the mapping, you’re using actual code in the form of data structures called “meta classes”

The simplest and coolest of these is called the ExpandoMetaClass!

Monday, June 7, 2010

Page 16: Groovy Metaprogramming for Dummies

All Groovy Objects Implement...

public interface GroovyObject {

Object invokeMethod(String name, Object args);

Object getProperty(String propertyName);

void setProperty(String propertyName, Object newValue);

MetaClass getMetaClass();

void setMetaClass(MetaClass metaClass);}

(This the heartbeat of the MOP)

Monday, June 7, 2010

Page 17: Groovy Metaprogramming for Dummies

MetaClasses Implement these methods for a classand thereby define the behavior of the class.

Object invokeMethod(String name, Object args);

Object getProperty(String propertyName);

void setProperty(String propertyName, Object newValue);

The beauty of ExpandoMetaClass is that you can add methods and properties to classes simply by assigning

them - it expands indefinitely (like a Map).

Monday, June 7, 2010

Page 18: Groovy Metaprogramming for Dummies

So The Magic Is Revealed(dynamic dispatch and missing methods)

<?xml version="1.0" encoding="UTF-8"?><books xmlns:meta="http://meta/book/info" count="3"> <book id="1"> <title lang="en">Groovy in Action</title> <meta:isbn>1-932394-84-2</meta:isbn> </book> <book id="2"> <title lang="en">Groovy Programming</title> <meta:isbn>0123725070</meta:isbn> </book> <book id="3"> <title>Groovy &amp; Grails</title> <!--Not yet available.--> </book> <book id="4"> <title>Griffon Guide</title> </book></books>

def builder = new StreamingMarkupBuilder()builder.encoding = 'UTF-8'def books = builder.bind { mkp.xmlDeclaration() namespaces << [meta:'http://meta/book/info'] books(count: 3) { book(id: 1) { title lang:'en', 'Groovy in Action' meta.isbn '1-932394-84-2' } book(id: 2) { title lang:'en', 'Groovy Programming' meta.isbn '0123725070' } book(id: 3) { title 'Groovy & Grails' // & is converted to &amp; comment << 'Not yet available.' } book(id: 4) { mkp.yieldUnescaped '<title>Griffon Guide</title>' } }}

println XmlUtil.serialize(books)

Monday, June 7, 2010

Page 19: Groovy Metaprogramming for Dummies

When it comes to Groovy’s syntax...What you can write is fixed by the

parser (like most languages)...

but there’s lots of optional stuff

optional “()“, optional “;”, optional “[]” (sometimes)...

so much optional it sounds nuts!

but there’s a method to the madness.

Monday, June 7, 2010

Page 20: Groovy Metaprogramming for Dummies

Combining the MOP with Groovy’s loose syntax...

You can highjack the meaning behind what the evaluator thinks

are method or property calls

but which supericially look more “declarative” than imperative

more “what” than “how”

the result can be shorter and clearer than java

Monday, June 7, 2010

Page 21: Groovy Metaprogramming for Dummies

Grails uses this stuff a lot...It has a lot of these “domain specific languages” or

“DSL”s

but this talk is about groovy it’s not about grails

But grails is the best example of where these approaches are used all over the place to do amazing things with just a

little code...so this talk is about grails

DSL

But it’s notit’s about how grails does things under the covers

about how *groovy* does things under the covers

so this is a meta talk about grailsExactly (exact-o-mundo)

Monday, June 7, 2010

Page 22: Groovy Metaprogramming for Dummies

And DSLs aren’t the only thing

The combination of closures and metaclasses can allow much coolness including:

AOP

IOC

Memoization

RMI via REST

(well, at least the factory pattern)

(a fancy word for caching)

(how many acronyms you gonna use?)

(without the AOP)(coolness = productivity)

Monday, June 7, 2010

Page 23: Groovy Metaprogramming for Dummies

The preceding will be demonstrated with some delightful code examples.

But first...

Monday, June 7, 2010

Page 24: Groovy Metaprogramming for Dummies

A Review of Closures for Newbies...

A java assignment:int val = 3;

A java function:public int func(int a, int b) { return a + b;}

Or in Groovy:def val = 3

A groovy function:def func(a, b) { a + b}

Monday, June 7, 2010

Page 25: Groovy Metaprogramming for Dummies

A Review of Closures for Newbies...

def func = (a, b) { a + b}

Closures are anonymous functions that can be assigned as *values*...

From the previously slide you can *almost* guess the syntax:

Close! Above itʼs the variable that holds the name so itʼs on the left thatʼs correct. The wrong part is the parameters:

def func = { a, b -> a + b}

Groovyʼs syntax is nice because the “{“ and “}” clearly mark the “code block”.

Monday, June 7, 2010

Page 26: Groovy Metaprogramming for Dummies

A Review of Closures for Newbies...

def class Counter { int count

def increment = { count ++ }}

One more thing about closures - regarding scoping...

A closure defined in the lexical scope of other code can refer to itʼs variables. This surrounding code is the “owner”:

But when closures are assigned to other objects, thereʼs more to life than just the surrounding code - esp. note the delegate:

this: as in Java, this refers to the instance of the enclosing class where a Closure is definedowner : the enclosing object (this or a surrounding Closure)delegate : by default the same as owner, but changeable for example in a builder or ExpandoMetaClass

Monday, June 7, 2010

Page 27: Groovy Metaprogramming for Dummies

A Review of Closures for Newbies...

def postiveOnly(Closure c) { list.each { val -> if(val > 0) c.call(val) }}

Oh! And one more thing about closures...Groovy has syntactic sugar which allows a function taking a closure as itʼs final argument can be called with the closure passed after the functions closing paren:

Can be called like so:

postiveOnly { val -> println “$val is > 0” }

And lastly: the default parameter if none is specified is simply “it”:

postiveOnly { println “$it is > 0” }

Monday, June 7, 2010

Page 28: Groovy Metaprogramming for Dummies

Finally: some real metaprogramming...

String.metaClass.shout = { delegate.toUpperCase(); }println "Groovy".shout()

Add a method to a class:

And yes, you could even:

(but just because you can doesnʼt mean you will)

This just shows what a flexible and powerful language groovy is.

postiveOnly { println “$it is > 0” }

String.metaClass.toUpperCase = { delegate.toLowerCase(); }println "Groovy".toUpperCase()

A programming language for grownups.

(with a silly name)

Monday, June 7, 2010