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.