Fuzion Logo
flang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

Currying and Partial Application

Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument (wikipedia). Similarly, partial application takes a function and creates a new function by fixing one or several of its arguments.

Curried form in Function definition

Languages like Haskell use the curried form to define functions with multiple arguments. Applied to Fuzion, the idea would be to declare a function like add

  add(x,y,z i32) i32 is x+y+z

as a chain of functions with single arguments

  add(x i32)(y i32)(z i32) i32 is x+y+z

or, using syntax closer to Haskell (but mixing Haskell's declaration and definition),

  add : x i32 -> y i32 -> z i32 -> i32 is x+y+z

The difference between a multi-argument declaration and a declaration using the curried form is just syntactic. Here is my attempt in collecting pros and cons:

multi-argument curried
example add(x,y,z i32) i32 is x+y+z

add(x i32)(y i32)(z i32) i32 is x+y+z


add : x i32 -> y i32 -> z i32 -> i32 is x+y+z

  • traditional look
  • similar to syntax of function type: (i32,i32,i32) -> i32
  • declaration syntax closer to call syntax (no parentheses) and similar to lambda ->
  • nicely maps to partial application syntax: addwh := add w h defines a function that would add w and h from its closure to the argument.

  • requires () in declaration, while call is add 1 2 3
  • confusing to newbies
  • declaration longer

The current syntax in Fuzion is as follows

element syntax
feature declaration add(x,y,z i32) i32 is ...
function type (i32, i32, i32) -> i32
feature call a := add x y z


a := add(x, y, z)
lambda x,y,z -> x+y+z


(x, y, z) -> x+y+z

Partial Application

Using Lambdas

Lambdas permit partial application of features, e.g.,

  add(x,y,z i32) i32 is x+y+z

  addwh i32 -> i32 := d -> add w h d

Using Partial Argument Lists

This syntax can be simplified when only the first arguments are fixed:

  addwh i32 -> i32 := add w h

Partial argument lists might make type inference easier than using lambdas such that it might be sufficient to write

  addwh := add w h

Using operators

Turning an infix operator into a function with one argument could be achieved by allowing its use as an prefix or postfix operator as follows: The lambdas assigned to f and g in this code

  f i32 -> i32 := x -> x - 3
  g i32 -> i32 := x -> 3 - x

Could be replaced using the infix operator as prefix- or postfix-operator to the partially applied argument 3:

  f i32 -> i32 := - 3
  g i32 -> i32 := 3 -

Alternatively, we could work with placeholders _ like Scala does

  f i32 -> i32 := _ - 3
  g i32 -> i32 := 3 - _

Automatic lambdas

When missing parameters automatically turn a call into a lambda using the partially applied call, it only makes sense to automatically create lambdas for fully applied calls as well. Take the following feature log that lazily evaluates its argument msg:

  log(msg () -> string) is
    if logLevel >= level
      say "LOG: {msg()}"

This would usually be called using a lambda:

  log ()->"passing lambda"

While a version with an argument to the lambda:

  logLocalized(msg (l language) -> string) is
    if logLevel >= level
      say "LOG: {msg default_language}"

could use a lambda

  logLocalized (l -> logtext.translate l)

or partial application:

  logLocalized logtext.translate

It would hence be consistent to automatically create the lambda in the first case as well:

  log "passing lambda implicitly"

In summary, any expression could be turned into a lambda if the expression is a partially applied call and the arguments expected for the target function type together with the partial actual arguments form a proper actual call or if the expression is not a partially applied call and the target function type expects no arguments.

One effect that this would have is that for the caller, there is no longer any syntactic difference between passing a value directly and passing a lazily evaluated closure.