Blocks

We can embed a sequence of statements inside "try", called a "block". Defined variables are only visible within that block, not outside:

def a = 'good morning'
try{
  def b = 'greetings', c = 'nice day'
                 //'def' keyword applies to both 'b' and 'c'

  assert a == 'good morning'
  assert b == 'greetings'
}
assert a == 'good morning'
//println b //a compile error if uncommented: b not visible here

Using the "def" keyword is optional because we are inside a script:

def c = 5
assert c == 5
d = 6
assert d == 6 //def keyword optional because we're within a script context
assert binding.variables.c == null
assert binding.variables.d == 6
           //when def not used, variable becomes part of binding.variables

But variables without "def" are visible outside the block:

try{
  h = 9
  assert binding.variables.h == 9
}
assert h == 9
assert binding.variables.h == 9

We can't define a variable (using "def") with the same name as another already visible (ie, another "in scope"):

def a = 'island'
//def a = 'snake' //a compile error if uncommented: a already defined
try{
  //def a = 'jewel' //a compile error if uncommented: a already defined
}

We can nest blocks:

def a = 123
try{
  try{
    try{
      assert a == 123
    }
  }
}

Closures

We can take a sequence of statements that refers to its external context and assign it to a variable, then execute it later. It's technically called a "closable block", commonly called a "closure":

def a = 'coffee'
def c = {
  def b = 'tea'
  a + ' and ' + b //a refers to the variable a outside the closure,
                  //and is remembered by the closure
}
assert c() == 'coffee and tea' //short for c.call()

The closure assigned to the variable (here, c) will remember its context (here, including a) even if that context is not in scope when the closure is called:

def c
try{
  def a = 'sugar'
  c = { a } //a closure always returns its only value
}
assert c() == 'sugar'
def d = c //we can also assign the closure to another variable
assert d() == 'sugar'

A closure always returns a value, the result of its last statement:

giveSeven = { 7 }
assert giveSeven() == 7 //value of last statement is returned

giveNull = { def a }
assert giveNull() == null //null returned if last statement has no value

By putting a closure within another, we can create two instances of it:

c = { def e = { 'milk' }; e }
d = c
assert c == d
v1 = c()
v2 = c()
assert v1 != v2

Closure Parameters

We can put parameters at the beginning of a closure definition, and pass values in when we call the closure:

def toTriple = {n -> n * 3}
assert toTriple.call( 5 ) == 15

We can also pass information out using the parameters:

def f = { list, value -> list << value }
x = []
f(x, 1)
f(x, 2,) //trailing comma in argument list OK
f(x, 3)
assert x == [1, 2, 3]

One parameter is always available, called "it", if no explicit parameters are named:

c = { it*3 }
assert c( 'run' ) == 'runrunrun'

If parameters aren't specified, "it" will still be implicitly defined, but be null:

//c = { def it = 789 }
          //a compile error when uncommented: 'it' already implicitly defined
c = { value1 -> def it = 789; [value1, it] }
          //works OK because no 'it' among parameters
assert c( 456 ) == [456, 789]
c = {-> def it = 789; it } //zero parameters, not even 'it', so works OK
assert c() == 789

Parameters can't have the same name as another variable in scope, except for the implicit parameter 'it':

def name= 'cup'
//def c={ name-> println (name) } //a compile error when uncommented:
                                  //current scope already contains name 'name'
c= { def d= { 2 * it }; 3 * d(it) }
      //'it' refers to immediately-surrounding closure's parameter in each case
assert c(5) == 30

If there's already a variable called 'it' in scope, we can access it using owner.it:

it= 2
c= { assert it == 3; assert owner.it == 2 }
c(3)

We can pass one closure into another as a parameter:

toTriple = {n -> n * 3}
runTwice = { a, c -> c( c(a) )}
assert runTwice( 5, toTriple ) == 45

We can return a closure from another:

def times= { x -> { y -> x * y }}
assert times(3)(4) == 12

There's a shortcut syntax when explicitly defining a closure within another closure call, where that closure is the last or only parameter:

def runTwice = { a, c -> c(c(a)) }
assert runTwice( 5, {it * 3} ) == 45 //usual syntax
assert runTwice( 5 ){it * 3} == 45
    //when closure is last param, can put it after the param list

def runTwiceAndConcat = { c -> c() + c() }
assert runTwiceAndConcat( { 'plate' } ) == 'plateplate' //usual syntax
assert runTwiceAndConcat(){ 'bowl' } == 'bowlbowl' //shortcut form
assert runTwiceAndConcat{ 'mug' } == 'mugmug'
    //can skip parens altogether if closure is only param

def runTwoClosures = { a, c1, c2 -> c1(c2(a)) }
    //when more than one closure as last params
assert runTwoClosures( 5, {it*3}, {it*4} ) == 60 //usual syntax
assert runTwoClosures( 5 ){it*3}{it*4} == 60 //shortcut form

Arguments in a closure call can be named. They are interpreted as the keys in a map passed in as the first parameter:

def f= {m, i, j-> i + j + m.x + m.y }
assert f(6, x:4, y:3, 7) == 20

def g= {m, i, j, k, c-> c(i + j + k, m.x + m.y) }
assert g(y:5, 1, 2, x:6, 3){a,b-> a * b } == 66

We can enquire the number of parameters for a closure, both from inside and outside the closure:

c= {x,y,z-> getMaximumNumberOfParameters() }
assert c.getMaximumNumberOfParameters() == 3
assert c(4,5,6) == 3

A closure may have its last parameter/s assigned default value/s:

def e = { a, b, c=3, d='a' -> "${a+b+c}$d" }
assert e( 7, 4 ) == '14a'
assert e( 9, 8, 7 ) == '24a' //override default value of 'c'

A closure can take a varying number of arguments by prefixing its last parameter with Object[], and accessing them using 'each':

def c = { arg, Object[] extras ->
  def list= []
  list<< arg
  extras.each{ list<< it }
  list
}
assert c( 1 )          == [ 1 ]
assert c( 1, 2 )       == [ 1, 2 ]
assert c( 1, 2, 3 )    == [ 1, 2, 3 ]
assert c( 1, 2, 3, 4 ) == [ 1, 2, 3, 4 ]

We can also prefix the last parameter of a closure with Closure[] to pass in a varying number of other closures, even using the shortcut syntax:

def apply = { a, Closure[] cc ->
  (cc as List).inject(a){ flo, it-> it(flo) }
          //apply the closures nestedly to the initial value
}
assert apply(7){it*3}{it+1}{it*2}.toString() == '44'

When we call a closure with a list argument, if there's no closure defined with a list parameter, the arguments are passed in as separate parameters:

def c= {a, b, c-> a + b + c}
def list=[1,2,3]
assert c(list) == 6

A closure may be copied with its first parameter/s fixed to a constant value/s, using curry:

def concat = { p1, p2, p3 -> "$p1 $p2 $p3" }
def concatAfterFly = concat.curry( 'fly' )
assert concatAfterFly( 'drive', 'cycle' ) == 'fly drive cycle'
def concatAfterFlySwim = concatAfterFly.curry( 'swim' )
assert concatAfterFlySwim( 'walk' ) == 'fly swim walk'

In closures, we can use currying and parameter-count-varying together:

def c = { arg, Object[] extras -> arg + ', ' + extras.join(', ') }
def d = c.curry( 1 ) //curry first param only
assert d( 2, 3, 4 ) == '1, 2, 3, 4'
def e = c.curry( 1, 3 ) //curry part of Object[] also
assert e( 5 ) == '1, 3, 5'
def f = e.curry( 5, 7, 9, 11 ) //currying continues on Object
assert f( 13, 15 ) == '1, 3, 5, 7, 9, 11, 13, 15'

We can make closures recursive:

def gcd //predefine closure name
gcd={ m,n-> m%n==0? n: gcd(n,m%n) }
assert gcd( 28, 35 ) == 7

We can even make a recursion of anonymous closures (thanks to 'call' method available for each closure)

def results = [];
{ a, b ->
  results << a
  a<10 && call(b, a+b)
}(1,1)
assert results == [1, 1, 2, 3, 5, 8, 13]  // Fibonacci numbers

Functions

A function is similar to a closure, though a function can't access defined variables in its surrounding context:

a = 32 //def keyword not used for this one
def c = 'there', d = 'yonder'
def f(){
  assert a == 32 //outer 'a' visible because 'def' keyword wasn't used with it
  def c = 'here'
    //compiles OK because other defined c invisible inside function definition
  //println d //a compile error when uncommented: d not accessable
  c
}
assert f() == 'here' //syntax to invoke a function

The def keyword is compulsory when defining functions:

def f(){
  a = 1
  c = { 'here, again' }
  c()
}
assert f() == 'here, again'
//g(){ println 'there, again' }
    //a compile error when uncommented: def keyword required

We use a special syntax to assign a function to another variable when using the original definition name:

def f(){ 77 } //define function using name 'f'
assert f() == 77
def g = this.&f //special syntax to assign function to another variable
assert g() == 77
def h = g //don't use special syntax here
assert h() == 77
f = 'something else' //this 'f' is a VARIABLE, not the function NAME
assert f() == 77 //the function name can't be reassigned

Unlike blocks and closures, we can't nest functions:

def f(){
  //def g1(){ println 'there' }
      //a compile error when uncommented: can't nest functions
  'here'
}
assert f() == 'here'
try{
  //def g2(){ println 'yonder' }
      //a compile error when uncommented: can't nest functions
}
c = {
  //def g3(){ println 'outer space' }
      //a compile error when uncommented: can't nest functions
}
def h(){
  try{ def c = { 'here, again' } }
      //we can have blocks and closures within functions
}

Function Parameters

A function can have parameters, with which we can pass information both in and out:

def foo( list, value ){
  list << value
}
x = []
foo(x, 1)
foo(x, 2)
assert x == [1, 2]

We can have more than one function of the same name if they each have different numbers of (untyped) parameters.

def foo(value){ 'v1' }
def foo(list, value){ 'v2' }
assert foo(9) == 'v1'
assert foo([], 1) == 'v2'

A function returns a value, unless prefixed by void instead of def, when it always returns null:

def f1(){ 7 }
assert f1() == 7 //value of last statement is returned

def f2(){ return 8; 3 }
assert f2() == 8 //return explicitly using return

void f3(){ 10 }
assert f3() == null //null always returned

//void f4(){ return 9 }
      //a compile error when uncommented: can't use 'return' in a void function

When there's a method and closure with the same name and parameters, the method is chosen instead of the closure:

def c(){'method c'}
def c= {-> 'closure c'}
assert c() == 'method c'

def d(i){'method d'}
def d= {'closure d'}
assert d(9) == 'method d'

Some Similarities with Closures

We can use the shortcut invocation syntax for closure parameters:

def f(Closure c){ c() }
assert f{ 'heehee' } == 'heehee'

A function may have its last parameter/s assigned default value/s:

def dd( a, b=2 ){ "$a, $b" }
assert dd( 7, 4 ) == '7, 4'
assert dd( 9 ) == '9, 2'

Arguments in a function call can be named, interpreted as keys in a map passed in as first parameter:

def f(m, i, j){ i + j + m.x + m.y }
assert f(6, x:4, y:3, 7) == 20

def g(m, i, j, k, c){ c(i + j + k, m.x + m.y) }
assert g(y:5, 1, 2, x:6, 3){a,b-> a * b } == 66

A function can take a varying number of arguments by prefixing its last argument by Object[], and accessing them using each:

def c( arg, Object[] extras ){
  def list= []
  list<< arg
  extras.each{ list<< it }
  list
}
assert c( 1 )          == [ 1 ]
assert c( 1, 2, 3, 4 ) == [ 1, 2, 3, 4 ]

When we call a function with a list argument, if there's none defined with a list parameter, the arguments are passed in separately:

def x(a, b, c){a + b + c}
def list=[1,2,3]
assert x(list) == 6

We can call a function recursively by referencing its own name:

def gcd( m, n ){ if( m%n == 0 )return n; gcd(n,m%n) }
assert gcd( 28, 35 ) == 7