DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Groovy++ in action: statically typed dynamic dispatch

Groovy++ in action: statically typed dynamic dispatch

Alex Tkachman user avatar by
Alex Tkachman
·
Oct. 27, 10 · Interview
Like (0)
Save
Tweet
Share
16.29K Views

Join the DZone community and get the full member experience.

Join For Free

It already became common place to say that both dynamically and statically typed code have their own merits and drawbacks. Groovy builders (xml, markup or whatever) are huge example where dynamic dispatch wins big time compare to statically typed code in terms of expressiveness (except that it is not easy to mix typed and untyped code together) but lose in performance and static checks. Today I am going to show how we can build statically typed builder with Groovy++ without paying such penalties.

Groovy++ already contains unique feature to combine statically and dynamically typed code together. If you annotate your method (or class or whole script) with @Typed(TypePolicy.MIXED) then compiler will not complain about unresolved methods and properties but instead will generate dynamic invocations, which will be dispatch via meta class.

That still does not solve problems of type checking and performance. Even worse it might introduce new runtime bugs as we under impression that we are protected by compiler, so in this article we present different and better approach.

Let us start with example, which is inspired by Groovy Enhancement Proposal 7 - JSON Support.

We want to build and print in to standard output JSON representation of personal data of a person.

JsonClosure externalData = {
additionalData {
married true
conferences(['JavaOne', 'Gr8conf'])
projectRoles([
'Groovy' : 'Despot',
'Grails' : 'Commiter',
'Gaelyk' : 'Lead'
])
}

whaeverElseData (["xxxxxx", [x: 12, y:14], ['a', 'b', 'c']])
}

JsonBuilder2 builder = [new PrintWriter(System.out)]
builder.person {
firstName 'Guillaume'
lastName 'Laforge'
address (
city: 'Paris',
country: 'France',
zip: 12345,
)

externalData ()
}

And here is expected JSON output

{
"person" : {
"firstName" : "Guillaume",
"lastName" : "Laforge",
"address" : {
"city" : "Paris",
"country" : "France",
"zip" : 12345
},
"additionalData" : {
"married" : true,
"conferences" : [ "JavaOne", "Gr8conf" ],
"projectRoles" : {
"Groovy" : "Despot",
"Grails" : "Commiter",
"Gaelyk" : "Lead"
}
},
"whaeverElseData" : [ "xxxxxx", {
"x" : 12,
"y" : 14
}, [ "a", "b", "c" ] ]
}
}

Now we come to the most interesting question: how can such dynamic behavior be implemented in statically compiled language.

This is surprisingly simple: when compiler finds the method call, which it can not resolve, it starts looking for a method with special name "invokeUnresolvedMethod" which has absolutely any return type and absolutely any parameters as long as first parameter is of type String.

If the method not found in current class it check outer one (of course, if such outer class exists). That allows to use this technique in nested closures.

If such a method found compiler will try to generate code to invoke it. If not, it will provide compile time error.

The main beauty of this approach is that we can be as specific as we want about parameters and return types, so all power of type inference and type checking is at our disposal.

Let us see how it works. Our implementation of JsonBuilder is surprisingly simple

class JsonBuilder2 {
protected static ThreadLocal current = []

private final MappingJsonFactory factory = []
private final JsonGenerator gen

JsonBuilder2(Writer out) {
gen = factory.createJsonGenerator(out)
gen.useDefaultPrettyPrinter()
}

void call(JsonClosure obj) {
try {
current.set(gen)

gen.writeStartObject()
obj ()
gen.writeEndObject()
gen.close ()
}
finally {
current.remove()
}
}

void invokeUnresolvedMethod(String name, JsonClosure obj) {
call {
gen.writeObjectFieldStart name
obj ()
gen.writeEndObject()
}
}
}

Underneath we use brilliant Jackson framework for handling JSON.

The one, who is not familiar with Groovy/Groovy++, may note how easy to reuse existing Java libraries.

Our builder is very simplified, so we have only two forms of using it. The one is simplified as we used above and another one is more generic. We can rewrite example above in more generaic foram as

builder = [new PrintWriter(System.out)]
builder {
person {
firstName 'Guillaume'
lastName 'Laforge'
address (
city: 'Paris',
country: 'France',
zip: 12345,
)

externalData ()
}
}

It is very important to notice that there is huge difference between first and second form.

builder.person{...} means builder.invokeUnresolvedMethod("person", (JsonClosure){...}) and

builder {...} means builder.call((JsonClosure){...})

Now to complete our story we need to implement JsonClosure. It is a very staright forward and I will omit part of the code below. The whole code can be found at Groovy++ repository

abstract class JsonClosure {

protected JsonGenerator gen

abstract void define ()

void call () {
if(!gen)
gen = JsonBuilder2.current.get()

if(!gen)
throw new IllegalStateException("Can't use JsonClosure outside of JsonBuilder")

define ()
}

void invokeUnresolvedMethod(String name, Object obj) {
if(obj == null) {
gen.writeNullField name
return
}

switch(obj) {
case Closure:
gen.writeObjectFieldStart(name)
obj.call()
gen.writeEndObject()
break

case JsonClosure:
gen.writeObjectFieldStart(name)
obj.gen = gen
obj.define()
obj.gen = null
gen.writeEndObject()
break

case String:
gen.writeStringField(name, obj)
break

case Number:
gen.writeNumberField(name, obj)
break

case Map:
gen.writeObjectFieldStart(name)
for(e in obj.entrySet()) {
invokeUnresolvedMethod(e.key.toString(), e.value)
}
gen.writeEndObject()
break

case Iterable:
gen.writeArrayFieldStart(name)
iterate(obj)
gen.writeEndArray()
break

case Object []:
invokeUnresolvedMethod(name, obj.iterator())
break

case Boolean:
gen.writeBooleanField(name, obj)
break

default:
gen.writeObjectField(name, obj)
break
}
}

void iterate(Iterable obj) { ........... }
}

That's almost it. Of course, more involved builders can also use getUnresolvedProperty(String) and setUnresolvedProperty(String,SomeType) which allows to translate unresolved property access in to fast dynamic calls (This feature is not yet implemented in the trunk)

I hope it was interesting and maybe for someone open new view angle on dynamic dispatch.

Thank you for reading and till next time.

Groovy (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Integrate AWS Secrets Manager in Spring Boot Application
  • Steel Threads Are a Technique That Will Make You a Better Engineer
  • When Should We Move to Microservices?
  • What Are the Different Types of API Testing?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: