Expression language functions

RiskScape allows you to define custom functions using the built in RiskScape expression language. This can be helpful if you want to reuse or highlight a particular expression within your project.

Note

Expression language functions are currently included in the Beta plugin.

Take the following bit of a pipeline as an example:

select({
    *,
    if(
        loss < 0,
        then: '-$' + abs(round(loss, 2))
        else: '$' + round(loss, 2)
    ) as formatted_loss
})

This bit of pipeline formats the numeric loss as a formatted string value showing the dollar amount so that it looks nice in a report or in a spreadsheet. This intent of this expression might not be obvious to a casual reader, and while commenting can make that clearer, turning it in to an expression function can also be a good option. It will also allow the function to be reused across models within your project.

We can convert this to an expression function by adding this to your project:

[function format_dollars]
framework = expression
description = Convert a floating point value in to a formatted dollar amount string
argument-types = [floating]
return-type = text
source = """
(val) ->
  if(val < 0,
    then: '-$' + str(abs(round(val, 2))),
    else: '$' + str(round(val, 2)))
"""

With this in your project, we can test it on the command like so:

$ riskscape expression eval "format_dollars(-2.5555)"
-$2.56

Using this new function, our pipeline code now becomes a lot simpler, like this:

select({*, format_dollars(loss) as formatted_loss})

argument-types

Expression functions can be defined with or without an argument-types parameter. When given, these types are displayed in the function’s reference and are used to validate the use of the function within another expression.

If we tried to give our format_dollars from the earlier example some text instead of a floating point number, it would fail with a type mismatch error.

It is also valid to leave out the argument-types parameter entirely and let the function’s body accept whatever arguments it’s given and decide whether it works or not.

return-type

The return-type parameter is also optional for expression functions, but can be useful when you want to force a particular type to be returned from your function.

Be aware that if the value your expression returns can not be converted in to the desired return-type, then your model will fail at that point.

Chaining complex expressions

In addition to using if() and switch() functions for control logic, you can also use the map() function for cases where you might want to ‘chain’ multiple expressions together.

For example, the following code is a customized spatial sampling function that always returns both the max and the average hazard intensity value. It does this by using map() to take the result of one expression (i.e. the sample() result), and then apply another expression to that (taking the mean() and max()).

[function custom_sample]
framework = expression
source = '''
(exposure, coverage) ->
  map({
        # first sample all intersections and turn that into a list of sampled hazard values
        all_values: map(sample(exposure, coverage), h -> h.sampled)
      },
      # then take the list of sampled values and return the mean and max
      sampled -> {
          max: max(sampled.all_values),
          mean: mean(sampled.all_values),
      })
'''

Nullable arguments

If argument-types is not defined, expression functions implicitly define each argument to be of type nullable(anything). This means the function will be called even if null values are given.

Tip

For more information about how RiskScape handles nulls when calling functions, see Function arguments and nullable

As an example, comment out or remove the argument-types line from the format_dollars example from earlier. If you run riskscape function info format_dollars you will see that the val argument is now defined as nullable(anything), instead of the previous floating type.

Evaluate the function with a null argument and see what happens:

$ riskscape expression eval "format_dollars(null_of('floating'))"
$

The function is called, even though the val argument is null, because RiskScape has respected the nullable(anything) type of the argument. The function’s expression is evaluated normally, allowing each function within the expression to handle the null individually. The if and the str function both handle nullable types and so are called, whereas abs and round are not. The str function then returns an empty string and our format_dollars function happily appends that to the dollar sign, resulting in the $ text that was printed out.