LEVIATHAN v962456e · 962456eee1

leviathan-lang.com / docs / learn

Functions, generics and injection

Functions and methods, named and default arguments, generic inference, and bind/inject.

Functions and arguments

A function has no this (it lives in a namespace or a class's static side); a method has one. Both overload by argument type, and both support named arguments and default parameters. A body is exactly one statement: use a block or an arrow.

Defaults and named arguments

int add(int a, int b) => a + b;

void greet(string name, string greeting = "Hello") {
    console.writeln("${greeting}, ${name}!");
}

console.writeln(add(2, 3));
greet("Ada");                       // "Hello, Ada!"
greet("Alan", greeting: "Hi");      // named argument reorders freely
5
Hello, Ada!
Hi, Alan!

Generics

Generics infer their type arguments from the call, and fall back to the target type:

Inferred type arguments

T firstOr<T>(Array<T> xs, T dflt) => xs.isEmpty() ? dflt : xs.first();

int  x = firstOr([1, 2, 3], 0);     // T = int, inferred from the argument
string s = firstOr([], "none");     // T = string, from the default
console.writeln(x);
console.writeln(s);
1
none

Injection

For wiring dependencies, bind supplies a value and inject requests one. A parameter you leave out of a call is filled from the nearest enclosing bind of its type. Binding is lexical and nearest-wins, and it must enclose the call site, not the callee: a bind inside a function body only reaches calls made from inside that body. That is what makes swapping a real dependency for a fake in tests a one-line change.

bind encloses the call, so swapping is one line

interface ILogger { void log(string s); }
class ConsoleLogger : ILogger { public void log(string s) => console.writeln(s); }
class QuietLogger : ILogger { public void log(string s) => console.writeln("(quiet) ${s}"); }

void audit(ILogger logger) {
    logger.log("audit ran");
}

void run() {
    bind ILogger => ConsoleLogger();   // in scope for the call below
    audit();                           // its ILogger parameter is filled by injection
}

void runQuietly() {
    bind ILogger => QuietLogger();     // the one line that changes
    audit();
}

run();
runQuietly();
audit ran
(quiet) audit ran