Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Groovy for Domain-Specific Languages, Second Edition

You're reading from  Groovy for Domain-Specific Languages, Second Edition

Product type Book
Published in Sep 2015
Publisher
ISBN-13 9781849695404
Pages 386 pages
Edition 1st Edition
Languages
Author (1):
Fergal Dearle Fergal Dearle
Profile icon Fergal Dearle

Table of Contents (20) Chapters

Groovy for Domain-specific Languages Second Edition
Credits
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Introduction to DSLs and Groovy Groovy Quick Start Essential Groovy DSLs The Groovy Language Groovy Closures Example DSL – GeeTwitter Power Groovy DSL Features AST Transformations Existing Groovy DSLs Building a Builder Implementing a Rules DSL Integrating It All Index

Chapter 5. Groovy Closures

In this chapter, we will focus exclusively on closures. We touched upon closures already in the previous chapter. Now, we will take a close look at them from every angle. Why devote a whole chapter of the book to one aspect of the language? The reason is that closures are the single most important feature of the Groovy language. Closures are the special seasoning that helps Groovy stand out from Java. They are also the single most powerful feature that we will use when implementing DSLs. In this chapter, we will discuss the following topics:

  • We will start by explaining just what a closure is and how we can define some simple closures in our Groovy code

  • We will look at how many of the built-in collection methods make use of closures for applying iteration logic, and see how this is implemented by passing a closure as a method parameter

  • We will look at the various mechanisms for calling closures, and we will take a look under the covers at how Groovy implements its...

What is a closure?


Closures are such an unfamiliar concept to begin with that it can be hard to grasp initially. Closures have characteristics that make them look like a method in so far as we can pass parameters to them and they can return a value. However, unlike methods, closures are anonymous. A closure is just a snippet of code that can be assigned to a variable and executed later:

def flintstones = ["Fred","Barney"]
def greeter = { println "Hello, ${it}" }
flintstones.each( greeter )
greeter "Wilma"
greeter = { }
flintstones.each( greeter )
greeter "Wilma"

Because closures are anonymous, they can easily be lost or overwritten. In the preceding example, we defined a variable greeter to contain a closure that prints a greeting. After greeter is overwritten with an empty closure, any reference to the original closure is lost.

Tip

It's important to remember that greeter is not the closure. It is a variable that contains a closure, so it can be supplanted at any time.

Given that greeter is a...

Closures and collection methods


In the last chapter, we encountered Groovy lists and saw some of the iteration functions, such as the each method:

def flintstones = ["Fred","Barney"]

flintstones.each {
    println "Hello, ${it}"
}

This looks like it could be a specialized control loop similar to a while loop. In fact, it is a call to the each method of Object. The each method takes a closure as one of its parameters, and everything between the curly braces {} defines another anonymous closure.

Closures defined in this way can look quite similar to code blocks, but they are not the same. Code defined in a regular Java or Groovy style code block is executed as soon as it is encountered. With closures, the block of code defined in the curly braces is not executed until the call() method of the closure is made:

println "one"
def two = 
{ 
println "two" 
}
println "three"
two.call()
println "four"

This will print the following:

one
three
two
four

Let's dig a bit deeper into the structure of each...

Closures as method parameters


We already know that parentheses around method parameters are optional, so the previous call to each can also be considered equivalent to:

flintstones.each ({ println "Hello, ${it}")

Groovy has special handling for methods whose last parameter is a closure. When invoking these methods, the closure can be defined anonymously after the method call parentheses. So, yet another legitimate way to call the preceding line is:

flintstones.each() { println "hello, ${it}" }

The general convention is not to use parentheses unless there are parameters in addition to the closure:

given:
    def flintstones = ["Fred", "Barney", "Wilma"]
when: "we call findIndexOf passing int and a Closure"
    def result = flintstones.findIndexOf(0) { it == 'Wilma'}
then:
    result == 2

The signature of the GDK findIndexOf method is:

int findIndexOf(int, Closure)

We can define our own methods that accept closures as parameters. The simplest case is a method that accepts only a single closure as...

Calling closures


In our previous examples, we were passing closures to the built-in collection methods. In the examples to date, we have deferred to the collection method to do the closure invocations for us. Let's now look at how we can make a call to the closure ourselves. For the sake of this example, we will ignore the fact that the GDK provides versions of the Thread.start method that achieves the same thing:

class CThread extends Thread {
    Closure closure
    
    CThread( Closure c ) { 
        this.closure = c
        this.start()
    }
    public void run() {
    if (closure)
        closure() // invoke the closure
    }
    
}

CThread up = new CThread(
    {
        [1..9]* each {
            sleep(10 * it) 
            println it
        }
    } )
    
CThread down = new CThread(
    {
    ["three","two", "one", "liftoff"]  each {
            sleep(100) 
            println it
        }
    } )

Here we define a subclass of the Java Thread class, which can be constructed with...

Finding a named closure field


All of the previous techniques for calling a closure rely on us having prior knowledge of a field or variable that contains a closure. This is fine for most straightforward applications of closures, but this book is about developing DSLs, so let's dig a little deeper.

Take the Grails application framework as an example. You can download Grails from http://grails.org/Download. Grails uses closures as a neat way of defining actions for its user interface controllers. No further configuration is required for the Grails runtime to be able to dispatch requests to an action:

class UserController {
….
    def login = {
       …. Login closure code
    }
}

We can implement a login action for our user controller in Grails—simply by declaring a closure in the controller class and assigning it to a field called login. In the UI, Grails provides tags to automatically create a link that will dispatch to our login action:

<g:link controller="user" action="login">Login<...

Closure parameters


In our previous examples, we have made use of the it keyword. When a closure accepts only a single parameter, we are able to refer to this parameter as it and are free from having to explicitly define the parameter. The possible syntax definitions for a closure are:

  • The default case allows any parameters to be passed to the closure:

    {
    // statements
    }
  • The closure does not accept any parameters:

    { -> 
    // statements
    }
  • The closure can accept one to many parameters with optional type annotations:

    { [type] param (,[type] param)* -> 
    // statements
    }

The parameter list is a comma-separated list of parameter names with optional type definitions. Closures behave slightly different depending on whether we supply the optional type:

given: "Closures with various parameter definition"
    def defaultParams = { println it; }
    def dynamicParams = { something -> println something; }
    def intParams = { int something -> println something; }
    def stringParams = { String something...

Closure return values


Closure declarations syntax provides no means of defining a return value. Every closure does, however, return a value with each invocation. A closure can have explicit return statements. If a return statement is encountered, then the value defined in the return statement is returned; otherwise, execution continues until the last statement in the closure block:

given: "a closure that returns values"
def closure = { param ->
    if (param == 1)
        return 1
    2
}
expect:
closure(1) == 1  // return statement reached
closure(-1) == 2 // ending statement evaluates to 2

If no return statement is encountered, then the value returned by the closure is the result of evaluating the last statement encountered in the closure block. If the last statement has no value, the closure will return null:

void voidMethod() {
}
given: "a closure returning void method"
def nullReturn = { voidMethod() }
expect:
nullReturn() == null

The closure scope


Closures have access to variables in their surrounding scope. These can be local variables or parameters passed to a method inside which the closure is defined. Here, we can access the name parameter and the local variable salutation in our closure:

def greeting ( name ) {
    def salutation = "Hello"
    def greeter = { println "$salutation , $name" }
    greeter()
}

when: "we call the greeting method"
    greeting("Dolly")
then:
    "Hello , Dolly" == output()

If the closure is defined within a class method, then the object instance fields are also available to the closure. The field member separator, shown in the following code, is also accessible within the closure:

class ClosureInClassMethodScope {
    def separator = ", "
    def greeting ( name ) {
        def salutation = "Hello"
        def greeter = { println "$salutation$separator$name" }
        greeter()
    }
}

given: "A class with a closure in a method"
ClosureInClassMethodScope greeter = new 
ClosureInClassMethodScope...

Summary


In this chapter, we covered closures in some depth. We covered all of the important aspects of working with closures. We explored the various ways to call a closure and the means of passing parameters. We saw how we can pass closures as parameters to methods, and how this construct can allow us to appear to add mini DSL syntax to our code.

Closures are the real "power" feature of Groovy, and they form the basis of most of the DSLs that we will develop later in this book. In the next chapter, we will build on this knowledge of closures and take a look at some more of the power features of the Groovy language, including builders and metaprogramming with the ExpandoMetaClass classes.

lock icon The rest of the chapter is locked
You have been reading a chapter from
Groovy for Domain-Specific Languages, Second Edition
Published in: Sep 2015 Publisher: ISBN-13: 9781849695404
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}