Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass a closure (without comma or parens) as a last parameter of a function in Groovy

Tags:

groovy

How can I call the label function and set a closure to it as the last parameter in this way? This code works only if I call label with parentheses but I don't need them. Is it possible to set the closure into function without them?

label = { name, callback ->
    callback()
}

label "lbl" {    // not works
    println "call $it"
}

label ("lbl") {  // works
    println "call $it"
}
like image 912
lancaster Avatar asked Jun 29 '18 16:06

lancaster


People also ask

What is -> in Groovy?

It is used to separate where you declare bindings for your closure from the actual code, eg: def myClosure = { x, y -> x + y } the part before -> declares that the closure has two arguments named x and y while the second part is the code of the closure.

What is a closure in Jules?

A closure is an anonymous block of code.

What is a closure in gradle?

A closure is a short anonymous block of code. It just normally spans a few lines of code. A method can even take the block of code as a parameter. They are anonymous in nature.


1 Answers

If you don't want to use parentheses then you have to split closure arguments with , to tell the compiler that you want to execute label with following two parameters:

label "lbl", {    
    println "call $it"
}

This notation is equivalent of:

label("lbl", {    
    println "call $it"
})

Groovy has this syntactic sugar that allows you passing closure outside parenthesis when closure is the last parameter of a function prototype, that's why following notation works:

label ("lbl") {
    println "call $it"
} 

But it is still an equivalent of the ones mentioned earlier.

How to make label "lbl" { } a valid statement?

If you really want to compile code like:

label "lbl" {    
    println "call $it"
}

you need to be aware of one thing - such notation is an equivalent of:

label("lbl" {    
    println "call $it"
})

It means that there is a function label that accepts single parameter - a value returned from lbl function that accepts a single parameter - closure. The implementation of these functions might look like this:

def label = { value ->
  // do something with value
  println "The value accepted by label function is '${value}'"
}

def lbl = { callback ->
  callback?.call()
}

label "lbl" {
  println "call $it"
}

It compiles and it prints following output to console:

call null
The value accepted by label function is 'null'

Of course as you can see you have to implement method lbl and in most cases this is very limiting - you can't call label function with any parameter different than lbl. You could implement Groovy's methodMissing(name, args) function that gets called whenever non-existing method gets invoked. Consider following example:

def methodMissing(String name, args) {

  if (args.length == 1 && args[0] instanceof Closure) {
    println "Running missing method '${name}'"
    return args[0].call()
  }

  throw new MissingMethodException(name, this.class, args)
}

def label = { value ->
  // do something with value
  println "The value accepted by label function is '${value}'"
}

def lbl = { callback ->
  callback?.call()
}

label "lbl" {
  println "call $it"
}

label "test" {
  println "nothing"
}

In this case when label "test" { println "nothing" } gets invoked, Groovy uses missingMethod because method test does not exist in the runtime context.

call null
The value accepted by label function is 'null'
Running missing method 'test'
nothing
The value accepted by label function is 'null'

However it doesn't mean, that you can think about this as an equivalent of getting rid of coma separator or parenthesis from your code. This is an example of a simple DSL written with Groovy that uses methodMissing mechanism to satisfy non-existing methods invocation. This last example is actually the same as:

label("test" {
  println "nothing"
})

A function that expects a single argument and in this case we provide it as a result of test method invocation with a single parameter { println "nothing" }.

I would strongly suggest accepting the fact that Groovy requires you to separate function arguments with a coma and don't use such DSL to remove coma or parenthesis from the code. Groovy DSL is very powerful and you have to know what you are doing when you decide using this tool in your code. For instance, think about what happens if you call:

label "test" { println "something" }

and for some reason a method defined as a following closure:

def test = { int num -> num + 2 }

exists in your class. You will start getting exceptions, because methodMissing is not called anymore in this case. Method test exists, but it does not have a signature that accepts a single closure.

like image 66
Szymon Stepniak Avatar answered Sep 20 '22 04:09

Szymon Stepniak