Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions and Calls

A function combines a parameter contract with a body expression:

let greet = (name:string):>void =>
    printl"Hello, {name}!"

:>void is the return contract. Dewy can infer a result from a body whose parameter types are already known:

let square = (value:int64) => value^2
let add = (left:int64 right:int64) => left + right

One parameter can omit parentheses. Zero parameters use ().

Positional and Named Arguments

Ordinary parameters may be supplied by position or name:

let describe = (name:string count:int64):>string =>
    "{name}: {count}"

describe("messages" 3)
describe(count=3 name="messages")

Dewy processes arguments from left to right. A positional argument fills the first parameter still open by position; a named argument fills that name.

Defaults Are Per Call

A default is used only if the completed call leaves its parameter unset:

let greet = (name:string greeting:string="Hello"):>void =>
    printl"{greeting}, {name}!"

greet("Ada")
greet("Grace" greeting="Welcome")
greet("Linus" "Hi")

The default expression evaluates separately for every call that needs it. A mutable value created by a default is not shared between callers.

A default does not remove its position. This matters when a required parameter follows one:

let combine = (left:int64 scale:int64=2 right:int64):>int64 =>
    left + scale * right

combine(10 3 16)
combine(10 right=16)

combine(10 16) supplies left and scale, then reports that right is missing.

Keyword-Only and Position-Only

A bare ... ends the positional run:

let connect = (host:string ... timeout_ms:int64):>void => {
    # ...
}

connect("example.test" timeout_ms=2_000)

Wrapping a parameter in <> makes its name private to the function's body and requires callers to use its position:

let increment = (<value:int64>):>int64 => value + 1

increment(41)

increment(value=41) is an error. A bare identifier in a function literal is always a parameter name, not an anonymous type annotation.

Function Contracts

A function type writes the same interface without a body and can be used anywhere another annotation can:

let apply = (
    transform:<(value:int64):>int64>
    value:int64
):>int64 => transform(value)

Names in a contract determine which keyword calls it accepts. Position-only parameters use the same <name:type> form in a function type as in a function literal. Dewy does not reinterpret a bare identifier as an unnamed type annotation.

A function that can fail lists its error values directly among its return alternatives, such as :>Customer | NotFoundError. There is no additional result wrapper around a successful return.

Overloads

& combines functions into an overload set. Argument contracts select the applicable alternative:

let format = ((value:int64):>string => "integer {value}")
           & ((value:string):>string => value)

format(42)
format("already text")

An unmatched or ambiguous call is an error.

Pipes

Pipes are calls written in data-flow order. The right operand is an ordinary expression, so a named function is selected with @ (bare, it would be called); a function literal pipes as it is:

3 |> @square
("Grace" greeting="Welcome") |> @greet
3 |> (x:int):>int => x * x

Grouping several piped arguments keeps any named bindings local to the group.

Functions as Values

Functions can also be passed, stored, and configured for a later call. Because a bare function name is always a call — a function with required parameters cannot even be mentioned without them — Dewy uses @ to select the function itself.

That topic builds on places, object members, and grouping, so it is developed later in Function Values and Composition. The Reference defines exact argument binding independently of those function-handle details.