Skip to content

Partial Application

Partial application lets us supply some of the arguments to a constructor, function, or behaviour, and get back something that lets us supply the rest of the arguments later.

A simple case

A simple case is to create a “callback” function. For example:

class Foo
  var _f: F64 = 0

  fun ref addmul(add: F64, mul: F64): F64 =>
    _f = (_f + add) * mul

class Bar
  fun apply() =>
    let foo: Foo = Foo
    let f = foo~addmul(3)
    f(4)

This is a bit of a silly example, but hopefully, the idea is clear. We partially apply the addmul function on foo, binding the receiver to foo and the add argument to 3. We get back an object, f, that has an apply method that takes a mul argument. When it’s called, it in turn calls foo.addmul(3, mul).

We can also bind all the arguments:

let f = foo~addmul(3, 4)
f()

Or even none of the arguments:

let f = foo~addmul()
f(3, 4)

Out of order arguments

Partial application with named arguments allows binding arguments in any order, not just left to right. For example:

let f = foo~addmul(where mul = 4)
f(3)

Here, we bound the mul argument but left add unbound.

Partial application is just a lambda

Under the hood, we’re assembling an object literal for partial application, just as if you had written a lambda yourself. It captures aliases of some of the lexical scope as fields and has an apply function that takes some, possibly reduced, number of arguments. This is actually done as sugar, by rewriting the abstract syntax tree for partial application to be an object literal, before code generation.

That means partial application results in an anonymous class and returns a ref. If you need another reference capability, you can wrap partial application in a recover expression. It also means that we can’t consume unique fields for a lambda, as the apply method might be called many times.

Partially applying a partial application

Since partial application results in an object with an apply method, we can partially apply the result!

let f = foo~addmul()
let f2 = f~apply(where mul = 4)
f2(3)