Roadmap
Upcoming types
- Restricted integer/string types with bounds
- Views on maps
- User-defined composite collections
- Relational / indexed collections
- Tree / graph / path types
- Advanced mutation and inner lensing
Block Argument Types
We can declare a block argument type in a type statement:
type Callback = { when: Time, msg: Str => Result(Str) }
These can’t be used in any value type expression. In particular, you can’t have a list of callbacks.
More commonly, though, block argument types can be directly shown as part of the function signature.
fun foreach-users(users: [User]) func: { user: User, number: Int => () } {
let mut num = 0;
for user in users {
func(user: user, number: (num += 1));
}
}
do {
foreach-users([user-1, user-2, user-3]) { user, number =>
print-line("User number \(number) has name \(user.name).")
}
}
Block types describe the argument names and types and the return type. Blocks aren’t normal values; they can’t be assigned to variables, but can only be constructed when passed to a function invocation.
Upcoming statements
- More powerful pattern matching on the left-hand side of assignments
- indexed iteration over lists
- iteration over map entries
Block parameters
Block parameters are special: they are defined with a type of the form { args => Return }
and are supplied at the call site with a block of code.
fun with-handler(input: Input, handle: { line: Str => Str }) => Output {
let out = \[]
for line in input.lines {
out ++= \[handle(line: line)]
}
return make-output(out)
}
// Blocks can be declared outside the parens
fun with-handler(input: Input) handle: { line: Str => Str } => Output {
...
}
Blocks cannot be stored in variables or returned: they are constructed only at the point of call.
Catch blocks
A catch block can attach to any statement, including the function body, and is invoked if there’s an escaping value.
if a {
...
} else if b {
...
} else {
...
} catch {
// catches anything escaping from
// the evaluation of `a` onward
}
It pattern matches like a when statement.
do {
let value = risky-operation() ! ok
...
} catch #miss -> {
...
} catch #invalid-input -> {
...
}
Future
Block arguments to functions
Block arguments are constructed at the call site:
let processed = with-handler(input) { line =>
reply " | " ++ line ++ " | "
}
Blocks can also be included inside the parens like a regular argument:
let processed = with-handler(input, { line =>
reply " | " ++ line ++ " | "
})
// Full arguments
let processed = with-handler(input: input) handle: { line =>
" | " ++ line ++ " | "
}
// Passing blocks inside the parens
let processed = with-handler(input, handle: { line =>
" | " ++ line ++ " | "
})
Upcoming error handling
- Catch blocks
- More structured panic / recovery mechanisms
neveras a capability passed to modules
- Support attaching stack traces or error return traces
- Support error algebra
- Support catching from labeled expressions
- Allow escaping from inner functions.
Future details
There are good technical reasons to have exception handling. Handling all errors by returning values on the stack requires a significant amount of machine code to branch on all the results. Centralized error handling through exceptions can have significant performance benefits for certain platforms.
We’d like to allow library authors to not care about this, and allow applications developers to centralize exception handling based on the measured behavior of their application.
Future feature: catch blocks
The idea behind a catch block is pretty similar to try / catch in exception based languages, except we’d like to be a bit more flexible with the syntax.
A catch block attaches to a preceding statement and operates very similarly to a when
statement. This looks very similar to a traditional try / catch statement:
do {
let value = possibly-not-ok() ! ok;
do-more-work();
} catch #miss -> {
handle-missing()
} catch #invalid-input -> {
handle-invalid()
}
catch blocks are sticky: they attach to regular statements.
let value = risky-operation() ! ok
catch #miss -> handle-missing();
It’s unclear how they attach to if / else chains.
We’d like catch blocks on loops able to use loop control flow:
for name in list-of-values {
interesting-operation() ! ok
} catch {
#keep-trying -> continue;
#give-up -> print-line("Unless continue is used, the loop terminates.");
#give-up-more-explicitly -> break;
#bail-out-entirely -> return;
}
On function definitions, a catch block is invoked before the function exits and must
return an acceptable value, or use the never assert.
fun regular-function(): ok ~ Value | error ~ Str {
return ok ~ new-value(interesting-operation() ! ok);
} catch {
#obscure-internal-error -> error ~ "helpful message";
#other-strange-error -> error ~ "strange message";
#not-possible -> never;
}