Feelin' Groovy: An Afternoon of Reflexive Metaprogramming

3,905 views

Published on

Given at the University of Mississippi Computer Science Seminar, September 2008.

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,905
On SlideShare
0
From Embeds
0
Number of Embeds
19
Actions
Shares
0
Downloads
0
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide
  • Of course, no introduction of myself would be complete without introducing my wife (CLICK!) And my four children (CLICK!)
  • Consider the following Groovy class….it’s a Duck, and as any good Duck should, it walks like a duck and quacks like a duck. (CLICK!) Now, consider this Groovy class….it’s a MentalPatient, and it just so happens to walk like a duck and quack like a duck. (CLICK!) Now, consider the Groovy class….it claims to walk with ducks. It defines one method, with an interesting type declaration in its signature (CLICK!) Here we have a dynamic type declaration….”def”….this allows us to pass in either a Duck or a MentalPatient…and to WalksWithDucks, as long as it walks like a duck…. (CLICK!) It’s a duck!!!
  • At the end of the day, it’s all in what you respond to! (CLICK!) Here we see one of Groovy’s metaprogramming constructs, the “respondsTo” method, which allows us to see whether or not a given object responds to certain method calls, irrespective of any class or interface implementation
  • In Groovy application, 3 kinds of objects: POJO’s (Plain Old Java Objects) -> regular Java objects created in Java or other JVM languages (JRuby, Jython, etc.) POGO’s (Plain Old Groovy Objects) -> written in Groovy, extend java.lang.Object, implement groovy.lang.GroovyObject Groovy interceptors -> written in Groovy and implement GroovyInterceptable invokeMethod(), getProperty(), setProperty() – make Groovy objects highly dynamic – can be used to work w/ methods and properties created on the fly getMetaClass()/setMetaClass() – make it very easy to create proxies to intercept and also inject methods on a POGO
  • Marker interface that extends GroovyObject All method calls – both existing and nonexisting methods – on an object implementing this interface are intercepted by its invokeMethod()
  • Groovy allows metaprogramming for POJO’s and POGO’s POJOS – Groovy maintains a MetaClassRegistry class of MetaClasses POGOS – direct reference to their MetaClass When you call a method: Groovy first checks whether the target is a POJO or POGO, and handles such calls differently If it is a POJO, Groovy fetches its MetaClass from the MetaClassRegistry and delegates method invocation to it. Any interceptors or methods you’ve defined on its MetaClass take precedence over the original method of the POJO For POGO’s, Groovy takes a few extra steps (CLICK!)
  • If the class implements GroovyInterceptable, all calls get routed to its invokeMethod() If not, then Groovy looks for the method first in the POGO’s MetaClass, and then if not found, in the POGO itself, executing in that order of preference If the POGO does not have either, it then looks for a property with that method name. If found, and if its type is Closure, it invokes the Closure. If no closure property, it invokes methodMissing() if it exists If not, it invokes the POGO’s invokeMethod() If all else fails, Groovy throws a MissingMethodException indicating the failure of the call
  • Suppose we have a Car class for which we want to call its check() method to perform filtering prior to any other method call CLICK! Here we implement invokeMethod()….CLICK! First, we check to see if the name of the method is not check()….if not, we get access to the check() MetaMethod and invoke it….CLICK! Next, we try to access the MetaMethod for the method that was called…CLICK! If we don’t get a null, we know we have a valid method, and we invoke it…CLICK! Otherwise, we delegate the call to the Car MetaClass’s invokeMethod()….the default implementation throwing a MissingMethodException….CLICK! Here’s some client code…we call the 3 valid methods and then one invalid method…CLICK! And here’s the output….notice that for start() and drive() we run the filter….but when check() is called directly, we do not….and again, notice the MissingMethodException on speed()
  • We can do the same thing using MetaClass…there are a few subtle differences: (CLICK!) We actually implement invokeMethod on the Car MetaClass by implementing a closure and assigning it to that method name. (CLICK!) Instead of passing “this” to invoke, we pass “delegate.” In a closure, delegate refers to the object that owns the closure. (CLICK!) Lastly, we call invokeMissingMethod() on the metaClass since we are already executing invokeMethod() and would not want to call that method recursively. invokeMissingMethod() actually delegates to invokeMethod() in the case of existing methods, and to missingMethod() in the case of nonexisting methods
  • We can also use MetaClass on a POJO…in this case the java.lang.Integer class (CLICK!) Again, we’re implementing a closure and assigning it to invokeMethod on the Integer class (CLICK!) The closure simply checks for a valid method call, invokes pre-filtering code, invokes the method, and invokes post-filtering code. Here is some client code….the first two calls exist on Integer, while the third does not… (CLICK!) And here is the output…..notice the execution of the filters for the valid method calls and then the MissingMethodException thrown on the call to empty()
  • So here we’re going to implement a StringUtil class containing a single method, toSSN(), that we’d like to be vailable on both String and StringBuffer…it will format the contents of the string as a social security number. So to do category injection, there are a few requirements: (CLICK!) First, you need a static method…(CLICK!) That accepts at least one parameter (the object for which the method is to be injected) (CLICK!) You write a use block, with the Category class as the argument…in this case, any Object inside the use block will have access to the toSSN() method because we left toSSN() dynamically typed (CLICK!) Objects outside the scope of the use block will not have toSSN()….(CLICK!) And here is the output…
  • I want to show you a second example of this to futher illustrate the flexibility….here we have a FindUtil class with an extractOnly() method….(CLICK!) Again, the method is static….(CLICK!) It takes the object (in this case a String) on which to inject the method as the first parameter….(CLICK!) And it also takes a second argument, in this case a closure that we expect to return a boolean value…(CLICK!) Inside the method, we iterate over each character in the String and append to the result only characters for which the closure returns true. You’ll see “it” here, which happens to be the default argument in closures if none are specified…(CLICK!) We setup our use block and execute the code (CLICK!) In this case, we pass in a closre that returns true only if the scanned character is a ‘4’ or a ‘5’….given the String of characters we start with, we get 54 as the output…
  • Here we’d like to add a method….daysFromNow()….to the Integer class…it will return an instance of java.util.Date equal to the Integer number of days from today….(CLICK!) Now you can tell I only made this slide a couple of days ago …. :-)
  • We can also add a property in a similar way by implementing the “getter” method for a property called daysFromNow….this allows us to drop the parenthesis, creating a more “fluent” or human readable interface, something you like to shoot for when building domain specific languages…CLICK! Here’s the output….
  • We can also add a static method by implementing methods on the static property of the metaClass…here’s we’ll add a method indicating whether or not its argument is an even number….CLICK! Again, here’s the output….
  • The last method type we can add in is a constructor…we do this by appending a closure to the existing set of constructors on Integer….in this case, we want to construct an Integer equal to the current day of the year…CLICK! And the output….
  • Let’s define a Person class that plays several sports…CLICK! First we’ll implement methodMissing()…CLICK! We’ll search for the suffix of the method named called….in this case we’re expecting method calls like “playFootball” or “playPolo”….CLICK! Having found it, we’ll return a String that says we’re “playing” the sport….here we’re using Groovy’s ability to execute arbitrary Groovy code embedded in a String inside the ${} construct…. CLICK! If we don’t find a legal method name, we throw a MissingMethodException….here’ some client code with several legal calls and one illegal call….CLICK! And here’s the output….notice that methodMissing() is not called for the work() method, but is for all of the sports-related methods….and we got a MissingMethodException for politics, which is usually not a sport, depending on who you’re talking to….
  • Now let’s implement the Intercept, Cache, Invoke pattern on our Person class…CLICK! First we intercept the method call, just as we did before. Having found it….CLICK! We implement a closure and add it to the Person’s MetaClass…again we’re using Groovy’s String evaluation capabilities to dynamically construct the method name….CLICK! We then return the result of the closure we just implemented….CLICK! Quickly notice the static initializer block….here I’m simply “touching” the metaClass for Person. The reason for this is an eccentricity in Groovy in that ExpandoMetaClass is not the default MetaClass for Groovy, however once you access it the first time it is replaced with an instance of EMC. This may be changed in future versions….CLICK! Here’s some client code…CLICK!….and here’s the output. Notice that methodMissing() is only called once
  • So here’s the beginning of our DSL….the first thing we do is enable the ExpandoMetaClass globally….this causes methods we add to a parent class, in this case Number, to be available to its children….CLICK! Next we reimplement Number’s get Property method….we assign it a closure that uses Jscience’s API to instantiate an Amount equal to the value of the Number instance and units specified by the propery name…. I’ve added a few examples of this and….CLICK!….here’s the output
  • The next step is to define our operator overloading….since Jscience doesn’t use the same operation names as Groovy, we need to implement a few closures to massage out the differences…..I’ve put here examples of Multiplication Division Addition Subtraction Powers Opposites And comparisons…and CLICK! Here’s the output…..notice on the power example we mixed metric and english units, and Jscience guessed that we’d want the output in metric units (indicated by the question mark)…
  • Our third step is to allow us to specify inverse units in a clean way, just like a physicist would. In a Groovy script like this one, all variables are local variables….however you can also pass in variables through a Binding, allowing for easy integration with existing Java code, etc. Here we’re going to implement a binding that will ignore references to the out property, which represents System.out….otherwise, it will return an amount equal to 1 unit of the symbol representing the property we tried to access…. Now, you can see these examples of inverse units, such as kilometers per hour, milligrams per Liter, etc…CLICK! And here’s the output of this portion….
  • Finally, we’d like to easily do conversions between different units, so we define a to() method on Amount’s metaclass….here’s some examples of that and CLICK! The output
  • Feelin' Groovy: An Afternoon of Reflexive Metaprogramming

    1. 1. Feelin’ Groovy: An Afternoon of Reflexive Metaprogramming Matt Stine UM-CIS Seminar September 3, 2008
    2. 2. Who Am I? <ul><li>Matt Stine </li></ul><ul><li>Ole Miss CIS Class of 2001 </li></ul><ul><li>Group Leader Research Application Development St. Jude Children’s Research Hospital </li></ul><ul><li>Memphis/Mid-South Java User Group Founder </li></ul><ul><li>Husband to Wendy (Ole Miss Mathematics 2001) </li></ul><ul><li>Father to Four (Abby, Isabella, Ali Kate, and Grant) </li></ul>
    3. 3. Agenda <ul><li>Brief introduction to Groovy </li></ul><ul><li>What is reflexive metaprogramming? </li></ul><ul><li>How does Groovy support reflexive metaprogramming? </li></ul><ul><li>What are some applications of reflexive metaprogramming? </li></ul><ul><li>A look at Internal DSL’s (Domain Specific Languages) </li></ul>
    4. 4. Groovy: A Brief Tour <ul><li>Agile, dynamic language for the JVM </li></ul><ul><li>Flexible, Java-like syntax </li></ul><ul><li>Additional power features inspired by languages like Python, Ruby and Smalltalk </li></ul><ul><li>Project founded by James Strachan and Bob McWhirter in 2003 </li></ul><ul><li>Became JSR-241 in 2004 </li></ul><ul><li>Now led by Guillaume Laforge and the company he co-founded, G2One, Inc. ( http://www.g2one.com ) </li></ul><ul><li>Currently at version 1.5.6 (also 1.6-beta-1) </li></ul><ul><li>http://groovy.codehaus.org </li></ul>
    5. 5. Groovy: A Brief Tour <ul><li>Absolutely everything is an object – no primitives! </li></ul><ul><li>Lots of syntactical sugar for lists, maps, ranges, regular expressions, etc. </li></ul>
    6. 6. Groovy: A Brief Tour <ul><li>Dynamic (duck) typing </li></ul>
    7. 7. Groovy: A Brief Tour <ul><li>Dynamic (duck) typing </li></ul><ul><ul><li>It’s all in what you “respond to!” </li></ul></ul>
    8. 8. Groovy: A Brief Tour <ul><li>Closures </li></ul><ul><ul><li>an anonymous chunk of code that may take arguments, return a value, and reference and use variables declared in its surrounding scope </li></ul></ul><ul><ul><li>“ resembles” anonymous inner classes in Java </li></ul></ul><ul><ul><li>In functional language parlance, such an anonymous code block might be referred to as an anonymous lambda expression </li></ul></ul><ul><li>Why Closures? </li></ul>
    9. 9. Groovy: A Brief Tour <ul><li>The traditional way: </li></ul>
    10. 10. Groovy: A Brief Tour <ul><li>A “groovier” way: </li></ul>
    11. 11. Groovy: A Brief Tour <ul><li>Metaprogramming </li></ul><ul><ul><li>Let’s go deeper…. </li></ul></ul>
    12. 12. Reflexive Metaprogramming? <ul><li>Metaprogramming: writing programs that write or manipulate programs as data (code generation) </li></ul><ul><li>Reflexive metaprogramming: writing programs that manipulate themselves as data </li></ul>Source: H. C. Cunningham. Reflexive metaprogramming in Ruby, Journal of Computing Sciences in Colleges, Vol, 22, No. 5, pp. 145-146, CCSC, May 2007.
    13. 13. How Groovy supports RM <ul><li>Meta Object Protocol (MOP) </li></ul><ul><ul><li>Groovy (like Ruby and Smalltalk) possesses a higher level concept called a MetaClass </li></ul></ul><ul><ul><li>The MetaClass decides the behavior of the actual class at runtime . </li></ul></ul><ul><ul><li>Anything is possible at runtime. </li></ul></ul>
    14. 14. How Groovy supports RM <ul><li>With Groovy’s MOP you can add, remove, replace: </li></ul><ul><ul><li>Static and instance methods </li></ul></ul><ul><ul><li>Constructors </li></ul></ul><ul><ul><li>Fields </li></ul></ul><ul><ul><li>Properties </li></ul></ul><ul><li>On any Groovy or Java class…. at runtime! </li></ul>
    15. 15. Groovy Object
    16. 16. Groovy interceptors
    17. 17. POJOs, POGOs, and their MetaClass
    18. 18. How Groovy Handles POGO Method Calls
    19. 19. How Groovy supports RM <ul><li>Categories (from Objective-C) </li></ul><ul><li>Open Classes (via ExpandoMetaClass ) </li></ul><ul><ul><li>Add methods to interfaces </li></ul></ul><ul><ul><li>Intercept method calls by overriding invokeMethod() </li></ul></ul><ul><ul><li>Provide “missing method” behavior by overriding methodMissing() </li></ul></ul><ul><ul><li>“ Borrow” methods from other classes </li></ul></ul><ul><ul><li>Dynamically construct method property names at runtime </li></ul></ul>
    20. 20. How Groovy supports RM <ul><li>Other constructs: </li></ul><ul><ul><li>Operator overloading </li></ul></ul><ul><ul><li>Method pointers </li></ul></ul><ul><ul><li>Closures </li></ul></ul><ul><ul><li>Expandos (objects on-the-fly) </li></ul></ul><ul><ul><li>Dynamic evaluation of Strings as Groovy code </li></ul></ul>
    21. 21. Three areas of focus: <ul><li>Method interception (Groovy AOP) </li></ul><ul><li>Method injection </li></ul><ul><li>Method synthesis </li></ul>
    22. 22. Groovy AOP <ul><li>An easy way to implement Aspect-Oriented Programming (AOP) like method interception or method advice </li></ul><ul><li>AOP? </li></ul><ul><ul><li>Separate “crosscutting concerns” into separate modules </li></ul></ul><ul><ul><li>These modules “advise” methods during program execution </li></ul></ul><ul><ul><li>Three types of advice: </li></ul></ul><ul><ul><ul><li>Before </li></ul></ul></ul><ul><ul><ul><li>After </li></ul></ul></ul><ul><ul><ul><li>Around </li></ul></ul></ul>
    23. 23. Two approaches: <ul><li>Let the object do it </li></ul><ul><ul><li>Implement the GroovyInterceptable interface </li></ul></ul><ul><ul><li>Not desirable if: </li></ul></ul><ul><ul><ul><li>You’re not the author of the class </li></ul></ul></ul><ul><ul><ul><li>The class is a Java class </li></ul></ul></ul><ul><ul><ul><li>You want to introduce interception dynamically </li></ul></ul></ul><ul><li>Let the object’s MetaClass do it </li></ul>
    24. 24. Using GroovyInterceptable
    25. 25. Using MetaClass
    26. 26. Using MetaClass (on a POJO)
    27. 27. Method Injection <ul><li>Adding behavior dynamically into classes </li></ul><ul><li>Two methods: </li></ul><ul><ul><li>Categories </li></ul></ul><ul><ul><li>ExpandoMetaClass </li></ul></ul>
    28. 28. Categories <ul><li>Controlled way to inject methods </li></ul><ul><li>Effect contained within a block of code </li></ul><ul><li>Alters your class’s MetaClass </li></ul><ul><ul><li>Within the scope of the block and executing thread </li></ul></ul><ul><ul><li>Change reversed on block exit </li></ul></ul><ul><li>Can be nested </li></ul><ul><li>Can apply multiple categories in a single block </li></ul>
    29. 29. Category Method Injection
    30. 30. Category Method Injection (2)
    31. 31. ExpandoMetaClass <ul><li>Groovy’s implementation of “open classes” </li></ul><ul><li>Can add to POGO’s and POJO’s: </li></ul><ul><ul><li>Methods </li></ul></ul><ul><ul><li>Properties </li></ul></ul><ul><ul><li>Constructors </li></ul></ul><ul><ul><li>Static methods </li></ul></ul><ul><li>Can borrow methods from other classes </li></ul><ul><li>Can do injection not only at the class level, but at the instance level (POGO’s only) </li></ul>
    32. 32. ExpandoMetaClass Injection <ul><li>Add a method: </li></ul>
    33. 33. ExpandoMetaClass Injection <ul><li>Add a property: </li></ul>
    34. 34. ExpandoMetaClass Injection <ul><li>Add a static method: </li></ul>
    35. 35. ExpandoMetaClass Injection <ul><li>Add a constructor: </li></ul>
    36. 36. Method Synthesis <ul><li>Create new methods with flexible and dynamic names “on-the-fly” </li></ul><ul><li>Names not decided ahead of time </li></ul><ul><li>Class users can decide names as long as they follow established conventions </li></ul><ul><li>Groovy provides methodMissing() to implement method synthesis </li></ul>
    37. 37. Method Synthesis
    38. 38. Method Synthesis <ul><li>There’s a catch… </li></ul><ul><li>Repeated calls to a nonexistent method involve repeated performance hits to evaluate </li></ul><ul><li>Make efficient by injecting the method on first invocation: </li></ul><ul><ul><li>“ Intercept, Cache, Invoke” pattern </li></ul></ul><ul><ul><li>Coined by Graeme Roche, Grails project lead </li></ul></ul>
    39. 39. Intercept, Cache, Invoke
    40. 40. Method Synthesis <ul><li>Can also be done using ExpandoMetaClass </li></ul><ul><ul><li>When? </li></ul></ul><ul><ul><ul><li>If you can’t modify the class </li></ul></ul></ul><ul><ul><ul><li>If the class is a Java class </li></ul></ul></ul><ul><ul><li>How? </li></ul></ul><ul><ul><ul><li>Implement methodMissing() on the class’s ExpandoMetaClass </li></ul></ul></ul><ul><li>Can also synthesize methods into specific instances of POGOs </li></ul>
    41. 41. A Look at Internal DSL’s <ul><li>From Martin Fowler: </li></ul><ul><ul><li>The basic idea of a domain specific language (DSL) is a computer language that's targeted to a particular kind of problem, rather than a general purpose language that's aimed at any kind of software problem. </li></ul></ul><ul><ul><li>This Internal DSL (also referred to as an embedded DSL) uses the constructs of the programming language itself to define to DSL. </li></ul></ul>
    42. 42. Why DSL’s? <ul><li>Use a more expressive language than a general programming language </li></ul><ul><li>Share a common metaphor between developers and domain experts </li></ul><ul><li>Have domain experts help design the business logic of an application </li></ul><ul><li>Avoid cluttering business code with too much boilerplate technical code </li></ul><ul><li>Cleanly seperate business logic from application code </li></ul>
    43. 43. Groovy + DSL’s <ul><li>Groovy’s metaprogramming constructs support the easy implementation of DSL’s!!! </li></ul><ul><li>Let’s take a look… </li></ul>
    44. 44. An Example DSL <ul><li>A mini-DSL for manipulating measures and units by leveraging the JScience library </li></ul><ul><li>From: Laforge, Guillaume. A Domain-Specific Languages for unit manipulations. http://groovy. dzone .com/news/domain-specific-language-unit- , March 2008. </li></ul>
    45. 45. What is JScience? <ul><li>Java library leveraging generics to represent various measurable quantities. </li></ul><ul><li>JSR-275: javax.measure.* reference implementation </li></ul><ul><li>Can be used for measuring mass, length, time, amperes, volts, … </li></ul>
    46. 46. How to represent a mass with JScience?
    47. 47. Wouldn’t it be nicer… <ul><li>If we could write this the way a physicist would write it? </li></ul><ul><li>What if we could write 3 kg + 2 kg ? </li></ul><ul><li>We’ll get close: </li></ul><ul><ul><li>def sum = 3.kg + 2.kg </li></ul></ul>
    48. 48. DSL Implementation
    49. 49. DSL Implementation
    50. 50. DSL Implementation
    51. 51. DSL Implementation
    52. 52. QUESTIONS?!?!?!
    53. 53. Resources <ul><li>http://groovy. codehaus .org </li></ul><ul><li>http://groovy. dzone .com </li></ul><ul><li>http://grails.org </li></ul>
    54. 54. References <ul><li>Rocher, Graeme. Rapid Web Development with Groovy and Grails: Module 1. Java University @ JavaOne 2007. </li></ul><ul><li>Rocher, Graeme. Dynamic Groovy: Groovy’s Equivalent to Ruby Open Classes. http: //graemerocher . blogspot .com/2007/06/dynamic-groovy-groovys-equivalent-to.html , June 2007. </li></ul><ul><li>Davis, Scott. Groovy Recipes: Greasing the Wheels of Java . Pragmatic Bookshelf, January 2008. </li></ul><ul><li>Davis, Scott. Groovy: The Red Pill. NFJS Gateway Software Symposium, March 2008. </li></ul><ul><li>Cunningham, H. C. Reflexive metaprogramming in Ruby, Journal of Computing Sciences in Colleges, Vol, 22, No. 5, pp. 145-146, CCSC, May 2007. </li></ul><ul><li>Almiray, Andres. MetaProgramming with Groovy I. http://groovy. dzone . com/articles/metaprogramming-groovy-I , January 2008. </li></ul><ul><li>Almiray, Andres. MetaProgramming with Groovy II. http://groovy. dzone . com/articles/metaprogramming-groovy-ii-expa , February 2008. </li></ul><ul><li>Laforge, Guillaume and John Wilson. Tutorial: Domain Specific Languages in Groovy. QCon 2007. http: //glaforge .free.fr/groovy/QCon-Tutorial-Groovy-DSL-2-colour. pdf </li></ul><ul><li>Laforge, Guillaume. A Domain-Specific Language for unit manipulations. http://groovy. dzone .com/news/domain-specific-language-unit- , March 2008. </li></ul><ul><li>Subramaniam, Venkat. Programming Groovy: Dynamic Productivity for the Java Developer . Pragmatic Bookshelf, March 2008. </li></ul><ul><li>Multiple Example Code Extracts and Figures from Programming Groovy . http://pragprog.com/titles/vslg . (Used with permission.) </li></ul><ul><li>Groovy User Guide. http://groovy.codehaus.org/User+Guide </li></ul>

    ×