8. Groovy basic structures and idioms¶
Nextflow is a domain specific language (DSL) implemented on top of the Groovy programming language, which in turn is a super-set of the Java programming language. This means that Nextflow can run any Groovy or Java code.
You have already been using some Groovy code in the previous sections, but now it's time to learn more about it.
8.1 Printing values¶
To print something is as easy as using one of the print
or println
methods.
snippet.nf | |
---|---|
The only difference between the two is that the println
method implicitly appends a new line character to the printed string.
Tip
Parentheses for function invocations are optional. Therefore, the following syntax is also valid:
snippet.nf | |
---|---|
Summary
In this step you have learned:
- How to print a string to the console
8.2 Comments¶
Comments use the same syntax as C-family programming languages:
Summary
In this step you have learned:
- How to write comments in Groovy
8.3 Variables¶
To define a variable, simply assign a value to it:
snippet.nf | |
---|---|
Local variables are defined using the def
keyword:
snippet.nf | |
---|---|
The def
should be always used when defining variables local to a function or a closure.
Summary
In this step you have learned:
- How to define variables in Groovy
8.4 Lists¶
A List object can be defined by placing the list items in square brackets:
You can access a given item in the list with square-bracket notation (indexes start at 0
) or using the get
method:
In order to get the length of a list you can use the size
method:
You can use the assert
keyword to test if a condition is true (similar to an if
function).
Here, Groovy will print nothing if it is correct, else it will raise an AssertionError message.
Exercise
This assertion should be correct, try changing it to an incorrect one.
Lists can also be indexed with negative indexes and reversed ranges.
Info
In the last assert line you are referencing the initial list and converting this with a "shorthand" range (..
), to run from the -1th element (2) to the 0th element (0).
List objects implement all methods provided by the java.util.List interface, plus the extension methods provided by Groovy.
Summary
In this step you have learned:
- How to define a list in Groovy
- How to access a list item
- How to apply methods
- How to use the
assert
keyword
8.5 Maps¶
Maps are like lists that have an arbitrary key instead of an integer. Therefore, the syntax is very much aligned.
snippet.nf | |
---|---|
Maps can be accessed in a conventional square-bracket syntax or as if the key was a property of the map.
Click the icons in the code for explanations.
snippet.nf | |
---|---|
- Using square brackets.
- Using dot notation.
- Using the
get
method.
To add data or to modify a map, the syntax is similar to adding values to a list:
snippet.nf | |
---|---|
- Using square brackets.
- Using dot notation.
- Using the put method.
Map objects implement all methods provided by the java.util.Map interface, plus the extension methods provided by Groovy.
Summary
In this step you have learned:
- How to define a map in Groovy
- How to access and update maps
8.6 String interpolation¶
String literals can be defined by enclosing them with either single- ('') or double- ("") quotation marks.
snippet.nf | |
---|---|
Info
Note the different use of $
and ${..}
syntax to interpolate value expressions in a string literal.
The $x
variable was not expanded, as it was enclosed by single quotes.
Exercise
Modify the script above to print Hello World
instead of $x $y
.
Finally, string literals can also be defined using the /
character as a delimiter. They are known as slashy strings and are useful for defining regular expressions and patterns, as there is no need to escape backslashes. As with double-quote strings they allow to interpolate variables prefixed with a $
character.
See the difference below:
Summary
In this step you have learned:
- How to define string literals in Groovy
- How to interpolate variables in string literals
8.7 Multi-line strings¶
A block of text that spans multiple lines can be defined by delimiting it with triple single or double quotes:
Finally, multi-line strings can also be defined with slashy strings. For example:
snippet.nf | |
---|---|
Info
Like before, multi-line strings inside double quotes and slash characters support variable interpolation, while single-quoted multi-line strings do not.
Summary
In this step you have learned:
- How to define multi-line strings in Groovy
8.8 If statement¶
The if
statement uses the same syntax common in other programming languages, such as Java, C, and JavaScript.
The else
branch is optional. Also, the curly brackets are optional when the branch defines just a single statement.
Tip
null
, empty strings, and empty collections are evaluated to false
.
Therefore a statement like:
snippet.nf | |
---|---|
Can be written as:
See the Groovy-Truth for further details.
Tip
In some cases it can be useful to replace the if
statement with a ternary expression (aka a conditional expression):
snippet.nf | |
---|---|
The previous statement can be further simplified using the Elvis operator:
snippet.nf | |
---|---|
Exercise
Write an if statement that prints Hello
if the variable x
is greater than 10 and Goodbye
if it is less than 10.
Summary
In this step you have learned:
- How to define an
if
statement in Groovy - How to use the ternary operator
- How to use the Elvis operator
8.9 For statement¶
The classical for
loop syntax is supported:
Iteration over list objects is also possible using the syntax below:
Summary
In this step you have learned:
- How to define a
for
loop in Groovy
8.10 Functions¶
It is possible to define a custom function into a script:
A function can take multiple arguments separating them with a comma.
The return
keyword can be omitted and the function implicitly returns the value of the last evaluated expression. Also, explicit types can be omitted, though not recommended:
Summary
In this step you have learned:
- How to define a function in Groovy
8.11 Closures¶
Closures are the Swiss army knife of Nextflow/Groovy programming. In a nutshell, a closure is a block of code that can be passed as an argument to a function.
A closure can also be used to define an anonymous function.
More formally, a closure allows the definition of functions as first-class objects.
snippet.nf | |
---|---|
The curly brackets around the expression it * it
tells the script interpreter to treat this expression as code. The it
identifier is an implicit variable that represents the value that is passed to the function when it is invoked.
Once compiled, the function object is assigned to the variable square
as any other variable assignment shown previously.
To invoke the closure execution use the special method call
or just use the round parentheses to specify the closure parameter(s):
As is, this may not seem interesting, but you can now pass the square
function as an argument to other functions or methods. Some built-in functions take a function like this as an argument. One example is the collect
method on lists:
Output | |
---|---|
By default, closures take a single parameter called it
.
To give it a different name use the ->
syntax. For example:
snippet.nf | |
---|---|
It’s also possible to define closures with multiple, custom-named parameters.
For example, when the method each()
is applied to a map it can take a closure with two arguments, to which it passes the key-value pair for each entry in the map
object. For example:
snippet.nf | |
---|---|
- Closure object that prints the key-value pair.
- Defines a map object with three entries.
- Invokes the
each
method passing the closure object.
A closure has two other important features.
First, it can access and modify variables in the scope where it is defined.
Second, a closure can be defined in an anonymous manner, meaning that it is not given a name, and is only defined in the place where it needs to be used.
As an example showing both these features, see the following code fragment:
snippet.nf | |
---|---|
- Defines a global variable.
- Defines a map object.
- Invokes the
each
method passing the closure object which modifies theresult
variable.
Learn more about closures in the Groovy documentation.
Summary
In this step you have learned:
- How to define a closure in Groovy
- How to invoke a closure
8.12 More resources¶
The complete Groovy language documentation is available at this link.
A great resource to master Apache Groovy syntax is the book: Groovy in Action.