Reference: functions

This page gives reference information on functions in DAML:

DAML is a functional language. It lets you apply functions partially and also have functions that take other functions as arguments. This page discusses these higher-order functions.

Defining functions

In Reference: expressions, the tubeSurfaceArea function was defined as:

tubeSurfaceArea : Decimal -> Decimal -> Decimal 
tubeSurfaceArea r h  =
  2.0 * pi * r * h

You can define this function equivalently using lambdas, involving \, a sequence of parameters, and an arrow -> as:

tubeSurfaceArea : BinaryDecimalFunction =
  \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h

Partial application

The type of the tubeSurfaceArea function described previously, is Decimal -> Decimal -> Decimal. An equivalent, but more instructive, way to read its type is: Decmial -> (Decimal -> Decimal): saying that tubeSurfaceArea is a function that takes one argument and returns another function.

So tubeSurfaceArea expects one argument of type Decimal and returns a function of type Decimal -> Decimal. In other words, this function returns another function. Only the last application of an argument yields a non-function.

This is called currying: currying is the process of converting a function of multiple arguments to a function that takes just a single argument and returns another function. In DAML, all functions are curried.

This doesn’t affect things that much. If you use functions in the classical way (by applying them to all parameters) then there is no difference.

If you only apply a few arguments to the function, this is called partial application. The result is a function with partially defined arguments. For example:

multiplyThreeNumbers : Int -> Int -> Int -> Int
multiplyThreeNumbers xx yy zz =
  xx * yy * zz

multiplyTwoNumbersWith7 = multiplyThreeNumbers 7

multiplyWith21 = multiplyTwoNumbersWith7 3

multiplyWith18 = multiplyThreeNumbers 3 6

You could also define equivalent lambda functions:

multiplyWith18_v2 : Int -> Int
multiplyWith18_v2 xx = 
  multiplyThreeNumbers 3 6 xx

Functions are values

The function type can be explicitly added to the tubeSurfaceArea function (when it is written with the lambda notation):

-- Type synonym for Decimal -> Decimal -> Decimal
type BinaryDecimalFunction = Decimal -> Decimal -> Decimal

pi : Decimal = 3.1415926535

tubeSurfaceArea : BinaryDecimalFunction =
  \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h

Note that tubeSurfaceArea : BinaryDecimalFunction = ... follows the same pattern as when binding values, e.g., pi : Decimal = 3.14159265359.

Functions have types, just like values. Which means they can be used just like normal variables. In fact, in DAML, functions are values.

This means a function can take another function as an argument. For example, define a function applyFilter: (Int -> Int -> Bool) -> Int -> Int -> Bool which applies the first argument, a higher-order function, to the second and the third arguments to yield the result.

applyFilter (filter : Int -> Int -> Bool)
    (x : Int)
    (y : Int) = filter x y

compute = scenario do
    assert (applyFilter (<) 3 2 == False)
    assert (applyFilter (/=) 3 2 == True)

    assert (round (2.5 : Decimal) == 3)
    assert (round (3.5 : Decimal) == 4)

    assert (explode "me" == ["m", "e"])

    assert (applyFilter (\a b -> a /= b) 3 2 == True)

The Folding section looks into two useful built-in functions, foldl and foldr, that also take a function as an argument.

Note

DAML does not allow functions as parameters of contract templates and contract choices. However, a follow up of a choice can use built-in functions, defined at the top level or in the contract template body.

Generic functions

A function is parametrically polymorphic if it behaves uniformly for all types, in at least one of its type parameters. For example, you can define function composition as follows:

compose (f : b -> c) (g : a -> b) (x : a) : c = f (g x)

where a, b, and c are any data types. Both compose ((+) 4) ((*) 2) 3 == 10 and compose not ((&&) True) False evaluate to True. Note that ((+) 4) has type Int -> Int, whereas not has type Bool -> Bool.

You can find many other generic functions including this one in the DAML standard library.

Note

DAML currently does not support generic functions for a specific set of types, such as Int and Decimal numbers. For example, sum (x: a) (y: a) = x + y is undefined when a equals the type Party. Bounded polymorphism might be added to DAML in a later version.