+

What is DataWeave? Part 4: Lambdas

10 min read
Was this tutorial helpful?
Thank you for your feedback!

We would like to thank MuleSoft Ambassador Joshua Erney for their contribution to this developer tutorial.

In the previous tutorial, we learned about variables, Boolean operators, flow control, and functions. For this tutorial, we’ll focus on a complex topic: lambdas (anonymous functions). We will also go through:

  • What are higher-order functions
  • How to use the dollar-sign syntax (which is widely used in DataWeave scripts)
  • How to apply the infix notations

Using named functions can be easier to understand and follow with the code. For this reason, you may not see a lot of lambdas explicitly declared in a DataWeave script. However, it is useful for you to get familiar with this concept because a lot of the DataWeave functions can be used with lambdas. Do not worry if you are not able to follow all the explanations in this tutorial, it is an abstract topic after all. You can revisit this tutorial as you continue your DataWeave learnings and the examples will look more and more familiar to you.

If this is your first time learning about DataWeave, we encourage you to check out this other tutorial to learn about the online DataWeave Playground. With this tool, you will be able to try out the scripts you will see in this tutorial right from your browser.

What are Lambdas?

DataWeave provides multiple ways to create functions. Just like we have named functions, we have functions without names (or anonymous functions), called lambdas. A lambda is a value in DataWeave, just like a String, an Object, or a Boolean. The syntax for a lambda is like so:

([<arg1>], [<arg2>], …, [<argN>]) -> body

Here’s how we might try to use a lambda in a DataWeave script. However, this script will result in an error.

Script

1
2
3
4
%dw 2.0
output application/json
---
() -> 2 + 3

Output

1
2
3
4
5
6
Cannot coerce Function to String

4| () -> 2 + 3
   ^^^^^^^^^^^
Trace:
  at main::main (line: 4, column: 1)

You may be wondering why the output was not 5. Why didn’t the function execute and return 2 + 3? Remember that functions represent functionality and therefore so do lambdas. They don’t do anything unless you call them, they’re just values. The lambda never executes; it’s describing functionality, not executing it. Because of this, 5 is not passed to the writer. Instead, DataWeave sends () -> 2 + 3 as a value (just like a String). In turn, the writer tries to serialize it as JSON, but JSON does not support functions, hence the error.

How would we get the script to return 5? Recall that in order to call named functions you need to use the following syntax:

<function_name>(<arg1>, <arg2>, …, <argN>)

But lambdas don’t have names, that’s the whole point! In order to force a lambda to execute, we surround it in parentheses and append () to the end.

Script

1
2
3
4
%dw 2.0
output application/json
---
(() -> 2 + 3)()

Output

1
5

The pair of parentheses at the end of the lambda works the same as when you call a named function. In other words, those parentheses are where you pass arguments to the function if you have any.

Script

1
2
3
4
%dw 2.0
output application/json
---
((n, m) -> n + m)(2, 3)

Output

1
5

If this syntax seems awkward, it’s because we’re using lambdas in a way they’re not meant to be used. If you needed to accomplish the above script, you’d be better off having the body as 2 + 3. What are lambdas good for? Because lambdas are values just like Strings, we can assign them to variables. This effectively gives a name to an unnamed function and allows us to use a lambda just like a normal function.

Script

1
2
3
4
5
%dw 2.0
output application/json
var add = (n, m) -> n + m
---
add(2, 3) 

Output

1
5

Higher-order Functions

But that’s not very useful either, we already have a nice syntax for the same thing with the fun keyword. The usefulness of lambdas becomes apparent when we combine two ideas:

  1. Lambdas are values just like Strings, Objects, and Booleans.
  2. Values can be passed to functions as arguments, as well as returned from functions.

In other words, lambdas become useful when you want to pass functions as arguments to other functions, or return a function from another function. A function that does this is referred to as a higher-order function (HOF). HOFs are easier to understand once you’re familiar with how to use one. Here’s an example of using a HOF, filter, to make sure an Array only contains odd numbers:

Script

1
2
3
4
5
6
7
%dw 2.0
output application/json
fun isOdd(n) =
  (n mod 2) == 1
var numbers = (1 to 5) // Generates [1, 2, ..., 5]
---
filter(numbers, (n, idx) -> isOdd(n))

Output

1
2
3
4
5
[
  1,
  3,
  5
]

The filter function takes two arguments: an Array (numbers) and a lambda. In situations like these, it’s important to specify what the lambda should do as well. In the case of filter, the lambda should take in two arguments: an item in the Array (n), and the index of that particular item (idx). It should return a Boolean. This Boolean value is used to determine if a value makes it to the returned Array. It is the responsibility of the receiving function to pass arguments into the lambda you specified, and do something with the return value. Let’s dig into how filter works to gain a deeper understanding of HOFs.

filter uses the lambda on each item of the Array to determine whether it should be in the returned Array. If filter calls the lambda with 1, it returns true, so 1 makes it to the output Array. If filter calls the lambda with 2, it returns false, so 2 does not make it to the output Array. This goes on until the last item of the Array is reached, then the final Array is returned.

While the code above is ok, it’s a little inconvenient. We had to give a name to the function in order to use it, even though we were never going to use it again. This is exactly where lambdas come in. They pass functions to other functions without the inconvenience of having to think up a name for them.

Script

1
2
3
4
5
%dw 2.0
output application/json
var numbers = (1 to 5) // Generates [1, 2, ..., 5]
---
filter(numbers, (n, idx) -> (n mod 2) == 1)

Output

1
2
3
4
5
[
  1,
  3,
  5
]

Infix Notation

So far, we’ve been calling filter using prefix notation. We learned in the previous tutorial the difference between the prefix and infix notations. In summary, with prefix notation, the function name comes before the arguments (add(1,2)). Meanwhile, the infix notation has the following syntax: <arg1> <function_name> <arg2>. Here’s how the code above would look if we called filter using infix notation:

Script

1
2
3
4
5
%dw 2.0
output application/json
var numbers = (1 to 5) // Generates [1, 2, ..., 5]
---
numbers filter ((n, idx) -> (n mod 2) == 1)

Output

1
2
3
4
5
[
  1,
  3,
  5
]

This may not seem like a great advantage, but it allows you to easily chain together functions that take in and return the same data type. For example, we can string together two filter functions in a way that is easy to read and understand:

Script

1
2
3
4
5
6
7
%dw 2.0
output application/json
var numbers = (1 to 5) // Generates [1, 2, ..., 5]
---
numbers
filter ((n, idx) -> (n mod 2) == 1) // filter odd numbers
filter ((n, idx) -> (n > 3)) // filter numbers > 3

Output

1
[5]

In this case, the Array is filtered on whether or not the value is an odd number, then filtered on whether or not the number is greater than 3. Notice the additional parentheses around the first lambda. The parentheses around the lambdas help DataWeave determine that the second call to filter is not part of the first filter’s lambda expression.

Dollar-sign ($) Syntax

HOFs are so prolific in DataWeave’s library that there are additional syntax features that make them easier to use. For functions that DataWeave provides, you can represent the first, second, and third arguments passed to the lambda as $, $$, and $$$, respectively. We’ll refer to this as the dollar-sign syntax. When you use the dollar-sign syntax, you do not need to specify the arguments of the lambda when you pass it to the function. Here’s our odd number filter example from earlier using the dollar-sign syntax:

Script

1
2
3
4
5
%dw 2.0
output application/json
var numbers = (1 to 5) // Generates [1, 2, ..., 5]
---
numbers filter (($ mod 2) == 1)

Output

1
2
3
4
5
[
  1,
  3,
  5
]

The dollar-sign syntax gives us all the same functionality as when we reference something by its name. This means we can chain selectors and indexes right on the dollar sign in order to query data.

Input payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
  {
    "id":1,
    "item":"cheese",
    "price":4.00
  },
  {
    "id":2,
    "item":"steak",
    "price":15.00
  },
  {
    "id":3,
    "item":"cereal",
    "price":5.00
  },
  {
    "id":4,
    "item":"apples",
    "price":2.00
  }
]

Script

1
2
3
4
%dw 2.0
output application/json
---
payload filter $.price > 5

Output

1
2
3
4
5
6
7
[
  {
    "id": 2,
    "item": "steak",
    "price": 15.00
  }
]

Next Steps

In this tutorial, we learned:

  • What are lambdas (anonymous functions)
  • What are higher-order functions
  • How to use the dollar-sign syntax
  • How to apply the infix notations to more examples using the filter function

It’s ok if lambdas are a bit hard to understand, it is a complex topic. The important part is to keep trying to learn more every day. You can always revisit this tutorial at a later time and it will slowly start making sense. In the next tutorial, we’ll talk about the type parameters, or generics (as known in other programming languages).

Click on the Next button below to continue to the next tutorial.

Previous Next

Try Anypoint Platform for free

Start free trial