"Narrowing" / Type Guards
Inspired by TypeScript's support for this, when you perform an instanceof
check on a variable with a oneof
type
within a conditional statement, Claro automatically "narrows" the type of the variable to the checked type. This is
logically valid because the only way that control-flow could possibly reach that context is if that was actually the
type at runtime.
Fig 1:
var intOrStr: oneof<int, string> = 10;
if (intOrStr instanceof int) {
# Claro automatically "narrows" the variable to have type `int`.
var addRes = intOrStr + 10;
print("{intOrStr} + 10 = {addRes}");
}
Output:
[0.003s][warning][perf,memops] Cannot use file /tmp/hsperfdata_runner/6 because it is locked by another process (errno = 11)
10 + 10 = 20
Fig 2:
var intOrStr: oneof<int, string> = 10;
if (intOrStr instanceof int) {
# Claro automatically "narrows" the variable to have type `int`.
var addRes = intOrStr + 10;
print("{intOrStr} + 10 = {addRes}");
# Claro automatically "widens" the variable to `oneof<int, string>`.
intOrStr = "ten";
addRes = intOrStr + 10; # <-- This is no longer valid.
}
Compilation Errors:
narrowing_EX2_example.claro:10: Invalid type: found <oneof<int, string>>, but expected one of (<int, long, float, double>).
addRes = intOrStr + 10; # <-- This is no longer valid.
^^^^^^^^
1 Error
Non-Trivial Example Usage
For a less trivial example of working with oneof
types, the below function is able to pretty-print a linked list by
checking if the current node is the end of the list or not by branching on the type of the next
reference:
Fig 3:
newtype LinkedNode<T> : struct {
val: T,
next: oneof<LinkedNode<T>, std::Nothing>
}
alias SB : string_builder::StringBuilder
function renderLinkedList<T>(head: LinkedNode<T>, sb: SB) -> SB {
_ = string_builder::add(sb, "{unwrap(head).val} -> ");
var next = unwrap(head).next;
if (next instanceof LinkedNode<T>) {
return renderLinkedList(next, sb); # <-- Type of `next` was "narrowed" to `LinkedNode<T>`.
} else {
return string_builder::add(sb, "*END*");
}
}
var linkedList = LinkedNode({val = 1, next = LinkedNode({val = 2, next = LinkedNode({val = 3, next = std::Nothing})})});
string_builder::create()
|> renderLinkedList(linkedList, ^)
|> string_builder::build(^)
|> print(^);
Output:
1 -> 2 -> 3 -> *END*
The above example relies on concepts described in later sections, so consider checking out User Defined Types and Generics for some more info.