Bosque Language Expressions
October 6, 2025 · View on GitHub
Expressions are a key component in Bosque programming. Thus, Bosque provides a rich set of expressions that support compact data manipulation and expression of intent. A major theme of these operators is to provide simple to reason about semantics that capture common operations with the goal of improving productivity and code quality.
Table of Contents
- Pure Bosque Expressions
- Literals
- Parameters/Variables/Captures
- Literal StringOf Expressions
- Literal Typed Expressions
- Namespace Constants
- Member Constants
- Tuple Constructors
- Record Constructors
- Entity Constructors
- Special Constructors
- Collection Constructors
- Namespace and Member Functions
- Namespace Operators
- Logical And/Or
- Tuple Index Access
- Record Property Access
- Field Access
- ITest Check
- ITest As and Conversion
- Method Call
- Method Call Virtual
- Prefix
!operator - Prefix numeric
-operator - Binary numeric arithmetic,
+/-/*//operators - Binary numeric comparison
==/!=/</<=/>/>=operators - Binary KeyType equality
===/!==operators - Binary Logic
&&/||/==>operators - MapEntry Constructor
=>operator - If-Then-Else Expression
- Bosque Expression Components
- ITests
- Arguments
- Binders
- Lambdas
- Direct Literals
- Regular Expressions
- Path Expressions
- Bosque Task Expressions
- Format Arguments
- Environment Variables
Pure Bosque Expressions
Literals
Unique Values
The keywords none, true, false are the unique literal representations for the None, and Boolean types.
none //special none value
true //true boolean literal
false //false boolean literal
Integral Numbers
The literals for Nat, Int, BigNat, BigInt are of the form [+-][0-9]+[n|i|N|I]. The n/i suffix is used for Nat and Int values, the N/I suffix is used for BigNat and BigInt values. Some examples include:
0n //0 as a Nat
0i //0 as an Int
-1I //-1 as an BigInt
100N //100 as a BigNat
Int and BigNat literals cannot have a leading - sign, duplicate signs are an error, and the sign is explicitly part of the number literal -- thus -2i and -(2i) are not the same semantically which is important for literal typedecl values such as -2i_Foo where this is the literal -2i as a Foo not the value 2i_Foo negated.
Real Approximation Numbers
The literals for Float and Decimal are of the form [+-][0-9]+[.][0-9]+[f|d]. The f suffix is used for Float values, which are 64bit IEEE values (excluding NaN and Infinities), the d suffix is used for base-10 Decimal floating point values (also excluding NaN and Infinities). Literal Rational values are of the form [+-][0-9]+(/[0-9]?R and
represent approximate rational values -- with a BigInt numerator and a Nat denominator. So infinite range and precision of up to 1/() which is rounded (TODO: what mode).
Some examples include:
0.5f //0.0 as a Float
5.2d //5.0 as a Decimal
5/2R //5/2 as a Rational
Common Special Numerics
xxxx
String Literals
xxx
Parameters/Variables/Captures
Variables in Bosque are of the form [_a-z][_a-zA-Z0-9]. Local variables can be declared using a let for immutable bindings or var for mutable bindings. Parameters are
always immutable -- except for this in a ref method and self in a ref method or action. Variables and parameters can be captured by lambda constructors and are immutable within the lambda scope. As Bosque is referentially transparent there are no modes that are needed for the lambda captures. See also let/var bindings.
Literal StringOf Expressions
Typed strings provide a direct way to expose otherwise latent information about the data that is stored in a string -- e.g. a zipcode, CSS attribute, part ID, etc. The format information is given by a Validator type (see Validator Types) and a conforming string literal.
typedecl ZipcodeUS = /[0-9]{5}(-[0-9]{4})?/;
...
"98052"ZipcodeUS
"98052-0001"ZipcodeUS
"abc123"ZipcodeUS //type error
Literal Typed Expressions
Typed literals provide a way to express structured information on other primitive data types, such as Bool, Int, Decimal, String, StringOf, UUID, DateTime, etc. These types are created using typedecl syntax (see Type Aliases) and literal values are constructed with the form <literal>_<Type>. Examples include:
Namespace Constants
Constant values can be declared in namespace scopes (see Namespaces). These constants can be used in expressions with the syntax <namespace>::<constant> and, when they refer to literal values they are then valid literal expressions as well (so can be used in switch statements etc.).
namespace Ns;
const c1: Int = 1i;
const c2: Int = 2i + Ns::c1;
Ns::c1 //1i and is a literal expression
Ns::c2 //3i but not a literal expression
Member Constants
Constant values can be declared in Object-Oriented scopes as well (see types). These constants can be used in expressions with the syntax <typename>::<constant> and, when they refer to literal values they are then valid literal expressions as well (so can be used in switch statements etc.).
In contrast to many languages const declarations are dynamically resolved. Thus, any subtype will also have access to the const declarations of the supertype. This allows for a more flexible and natural way to define common constants for a set of types.
concept Foo {
const c1: Int = 1i;
}
entity Bar provides Foo {
const c2: Int = 3i + Bar::c1;
const c3: Int = Foo::c1;
}
Foo::c1 //1i and is a literal expression
Bar::c1 //1i as well
Bar::c2 //3i but not a literal expression
Bar::c3 //1i and a literal expression as well (transitively)
Tuple Constructors
Bosque tuples are constructed using a basic syntax of [e1, e2, ..., em] where the ei are expressions. The type of the tuple is implied by the types of the ei expressions. Tuples cannot be subtypes of each other (see tuple types). Instead the type inference system will coerce the individual ei expressions to the needed types before constructing the tuple if it can.
Basic Tuples:
[] //empty tuple
[1i, 2i, 3i] //tuple of 3 Ints
[1i, [true]] //tuple of Int and another tuple
Tuple with Inference:
function foo(): [Int, Boolean?] {
return [3, true]; //expression types are Int and Boolean but inference converts to Int and Boolean?
}
Record Constructors
Bosque records are constructed using a basic syntax of {p1=e1, p2=e2, ..., pm=em} where the pi=ei are property/expression bindings. The type of the record is implied by the types of the pi=ei expressions. Records cannot be subtypes of each other (see record types). Instead the type inference system will coerce the individual ei expressions to the needed types before constructing the record if it can.
Basic Records:
{} //empty record
{f=1i, g=2i} //record of 2 Ints (f and g)
{f=1i, g={h=true}} //record of Int and another record
Record with Inference:
function foo(): {f: Int, g: Boolean?} {
return {f=3, g=true}; //expression types are Int and Boolean but inference converts to Int and Boolean?
}
Entity Constructors
Object-oriented programming in Bosque is centered around Concepts and Entities (see Types) which roughly correspond to objects and abstract classes/interfaces in other languages. These types can be defined explicitly using entity or concept declarations and are also implicitly created via typedecl or datatype declarations. Examples of simple OO construction are:
entity Foo {
field f: Int;
}
Foo{3i} //constructs a Foo where field f has value 3i
concept Bar {
field g: Int;
}
entity Baz provides Bar {
field h: Nat;
}
Baz{3i, 4n} //constructs a Baz where field g has value 3i and field h has value 4n
concept Named {
field name: String;
}
entity Qux provides Named, Bar {
}
Qux{"bob", 3i} //constructs a Qux where field name has value "bob" and field g has value 3i
Similarly object-oriented types can be defined as typedecls or datatypes and constructed using the same syntax. For example:
typedecl Fahrenheit = Int;
Fahrenheit{32i} //constructs a Fahrenheit value for freezing
typedecl SystemID = /[A-Z]{3}"-"[0-9]+/;
typedecl PartID = StringOf<SystemID>;
"X-52"_PartID //fails the invariant on the string
"ABC-123"_PartID //constructs a literal PartID value with the value ABC-123
PartID{SystemID::from("ABC-123")} //constructs a PartID value with the value ABC-123
datatype BoolOp using {
line: Nat
} of
| LConst { val: Bool }
| NotOp { arg: BoolOp }
| AndOp { larg: BoolOp, rarg: BoolOp }
| OrOp { larg: BoolOp, rarg: BoolOp }
;
NotOp{5n LConst{1n, false}} //constructs a NotOp value
In all cases they support the use of data invariants of various types (mostly using the invariant member). The invariants are checked on construction and result in an error when violated.
concept Bar {
field g: Int;
invariant $g > 0i;
}
concept Named {
field name: String;
invariant $name !== "";
}
entity Qux provides Named, Bar {
field h: Int;
invariant $g < $h;
}
Qux{"", 3i, 10i} //fails invariant $name !== ""
Qux{"bob", 0i, 10i} //fails invariant $g > 0
Qux{"bob", 4i, 2i} //fails invariant $g < $h
Qux{"bob", 4i, 10i} //ok
typedecl Percentage = Nat & {
invariant $value <= 100n;
}
Percentage{101n} //fails invariant $value <= 100n
Percentage{99n} //ok
Special Constructors
The Option<T> and Result<T, E> types have special constructor support. In addition to the regular Something<Int>{3i} constructor forms they provide short and type inferred forms with the syntax some(e), ok(e), err(e) and result(e) where e is an expression. These forms will infer the appropriate template types and convert the expressions as needed. The result(e) type will also insert conversions between compatible result types and construction of result objects from values (see issue #1).
Examples of these special forms include:
let x: Option<Int> = some(3i); //x is a Some<Int> with value 3i
let y: Option<Int?> = some(5i); //y is a Some<Int?> with value 5i
function foo(): Result<Int, String> {
return ok(3i); //returns a Result<Int, String> with value 3i
}
function bar(): Result<Int, String> {
return err("error"); //returns a Result<Int, String> with error "error"
}
function baz(): Result<Int, String?> {
return result(bar()); //returns a Result<Int, String?> by converting the return of bar into the correct type
}
Collection Constructors
Bosque provides a range of standard collections, including List<T>, Stack<T>, Queue<T>, Set<T>, and Map<K, V> (more details are available in the collections docs).These collections can be constructed using syntax similar to the entity constructors (but generalized since they may have many elements). For example:
List<Bool>{} //constructs an empty List<Bool>
List<Int>{1i, 2i, 3i} //constructs a List<Int> with values 1i, 2i, 3i
Map<Int, String>{} //constructs an empty Map<Int, String>
Map<Int, String>{MapEntry<Int, String>{1i, "one"}, MapEntry<Int, String>{2i, "two"}} //constructs a Map<Int, String> with entries 1i->"one" and 2i->"two"
Map (Set) constructors must have KeyType values as keys (see types) also do validity checking that there are no duplicate keys. Maps also provide a shorthand syntax for constructing MapEntry values:
Map<[Int, Int], String>{} //Type error [Int, Int] is not a KeyType
Map<Int, String>{MapEntry<Int, String>{1i, "one"}, MapEntry<Int, String>{1i, "two"}} //Error duplicate key values
Map<Int, String>{1i => "one", 2i => "two"} //constructs a Map<Int, String> with entries 1i->"one" and 2i->"two"
Namespace and Member Functions
Bosque supports simple functions defined at a namespace scope or within a type scope. Namespace functions can be called with or without a namespace qualifier (for functions defined in ths same namespace where they are use). Functions defined in a type scope must always be called with the type qualifier (even within the same type scope).
namespace Main;
function f(x: Int, y: Int): Int {
return x + y;
}
f(1i, 2i) //returns 3i
Main::f(1i, 2i) //returns 3i
entity Foo {
function f(x: Int, y: Int): Int {
return x - y;
}
}
Foo::f(1i, 2i) //returns -1i
Namespace Operators
[TODO] Operators allow multiple dynamic dispatch functions to be defined. They are currently partially implemented (see issue #13).
The declaration of an operator is a virtual or abstract definition which may have Concept or Union typed arguments. Each dispatch implementation must have only unique (non-entity and non-union) typed arguments and must be defined in the same namespace as the operator (this prevents resolution ambiguity and accidental overloading). Arguments may also use literal value dispatch on one argument in the operator. If these are used then each dispatch implementation must provide a literal value for this argument.
Tuple Index Access
Tuples are indexed using the syntax e.i where i is a literal integer value.
[TODO] Currently only expressions with a unique tuple type can be accessed. Adding virtual tuple access is an open issue and also impacts spread arguments.
Examples of tuple access include:
[1n, 2i].0 //returns 1n
[1n, 2i].1 //returns 2i
[[1n, 2i], 3i].0.1 //returns 2i
[1n, 2i].2 //error tuple has no index 2
Record Property Access
Records properties are accessed using the syntax e.p where p is a property name.
[TODO] Currently only expressions with a unique record type can be accessed. Adding virtual record access is an open issue and also impacts spread arguments.
Examples of record access include:
{f=1n, g=2i}.f //returns 1n
{f=1n, g=2i}.g //returns 2i
{f={g=1n, h=2i}, q=3i}.f.h //returns 2i
{f=1n, g=2i}.h //error record has no property h
Field Access
Fields in Object-Oriented types are accessed using the notation e.f where f is a field name. Fields accesses can be done on expressions of concrete and abstract types (virtual accesses). These virtual accesses can be on Concepts or Unions -- however all possible resolutions must be to the same definition!
Examples of field access include:
concept Bar {
field g: Int;
}
concept Named {
field name: String;
}
entity Qux provides Named, Bar {
field h: Int;
}
entity Qaz provides Named, Bar {
field h: Int;
}
let v1 = Qux{"bob", 1i, 2i};
let v2 = Qaz{"alice", 3i, 4i};
v1.g //concrete field lookup -- 1i
v2.g //concrete field lookup -- 3i
let x: Named = ...;
x.name //virtual field lookup
x.h //error -- Named does not have field h
let y: Qux | Qaz = ...;
y.g //virtual field lookup
y.h //error -- Qux, Qaz both have h fields but different declarations
ITest Check
Bosque provides a unique form of test operators for types/values that generalizes simple type-relations checks. These operators are also used to implement the explicit flow-typing and binding in the language. The full syntax/semantics for these operators are covered in the ITests section. Their use for postfix tests uses the following syntax e?ITest where ITest is an ITest expression.
Some examples of these tests include:
concept Bar {
field g: Int;
}
concept Named {
field name: String;
}
entity Qux provides Named, Bar {
field h: Int;
}
entity Qaz provides Named, Bar {
field h: Int;
}
let x: Named = ...;
x?<Qux> //true if x is a Qux
x?!<Qux> //true if x is a not a Qux
let y: Qux | Qaz | None = ...;
y?<Qux | Qaz> //true if y is a Qux or Qaz
y?<None> //true if y is None
y?none //true y is none
y?!none //true if y is none
y?some //true if y is a subtype of Some
ITest As and Conversion
Bosque provides a unique form of conversion operators for types/values that generalizes simple type-relations checks. These operators are also used to implement the explicit flow-typing and binding in the language. The full syntax/semantics for these operators are covered in the ITests section. Their use for postfix tests uses the following syntax e@ITest where ITest is an ITest expression.
Some examples of these tests include:
concept Bar {
field g: Int;
}
concept Named {
field name: String;
}
entity Qux provides Named, Bar {
field h: Int;
}
entity Qaz provides Named, Bar {
field h: Int;
}
let x: Named = ...;
x@<Qux> //fails if x is not a Qux and result type is Qux
x@!<Qux> //fail if x is a not a Qux and result type is Named
let y: Qux | Qaz | None = ...;
y@<Qux | Qaz> //fails if y is not a Qux or Qaz and result type is Qux | Qaz
y@!<Qux> //fails if y is a Qux and result type is Qaz | None
y@!<None> //fails if y is None and result type is Qux | Qaz
y@some //fails if y is none and result type is Qux | Qaz
let z: Result<Int, String> = ...;
z@ok //fails if z is err and result type is Int
z@err //fails if z is ok and result type is String
Method Call
Bosque Object-Oriented types support member method definitions. These may be direct (no virtual definitions or dispatch) or virtual. Direct methods are defined on a single type and called directly when the receiver is of that type or a subtype. As with field accesses, if there are multiple types in a union, a method may be invoked provided all possible resolutions are to the same definition.
Examples of direct method calls include:
concept Bar {
field g: Int;
method get_g(): Int {
return this.g;
}
}
entity Qux provides Bar {
field h: Int;
method get_h(): Int {
return this.h;
}
}
entity Qaz provides Bar {
field h: Int;
method get_h(): Int {
return this.h;
}
}
let v1 = Qux{"bob", 1i, 2i};
let v2 = Qaz{"alice", 3i, 4i};
v1.get_h() //1i
v2.get_h() //3i
let x: Bar = ...;
x.get_g() //call to Bar get_g
x.get_h() //error -- Bar does not have method get_h
let y: Qux | Qaz = ...;
y.get_g() //call to Bar get_g
y.get_h() //error -- differing declarations of get_h
Method Call Virtual
Bosque Object-Oriented types support virtual methods are defined on a single concept type using the abstract or virtual attribute. A virtual method may be defined again in subtypes using the override attribute. Virtual methods are dispatched based on the type of the receiver. However, if the receiver is a union type, all the possible resolutions must be to the same definition (although the implementations may differ).
Examples of virtual method calls include:
concept Bar {
field g: Int;
virtual method get_g(): Int {
return this.g;
}
abstract method get_h(): Int;
}
entity Qux provides Bar {
field h: Int;
override method get_h(): Int {
return this.h;
}
}
entity Qaz provides Bar {
field h: Int;
override method get_h(): Int {
return this.h;
}
override method get_g(): Int {
return this.g + 1i;
}
}
let x: Bar = ...;
x.get_g() //call to Qux get_g (goes to Bar impl) or Qaz get_g (goes to Qaz override impl)
x.get_h() //abstract in Bar so dispatches to Qux or Qaz
let y: Qux | Qaz = ...;
y.get_g() //call to Qux get_g (goes to Bar impl) or Qaz get_g (goes to Qaz override impl)
y.get_h() //abstract in Bar so dispatches to Qux or Qaz
Prefix Boolean Not
The ! operator is used to perform a boolean not operation on a boolean expression.
!true //false
!false //true
Prefix Negation
In Bosque the - operator is used to perform a negation operation on a numeric expression. In contrast to most languages the - operator is safe for all numeric types. Specifically, as the valid range for Int is symmetric from -( - 1) to ( - 1)!
-(-1i) // 1i
-(3/2R) // -3/2R
Binary numeric arithmetic operators
Bosque supports the standard set of binary numeric arithmetic operators of +, -, *, and /. These are defined for all numeric types and automatically for any typedecl of a numeric type. The fixed size Int and Nat types are checked for overflows while the Nat and BigNat types are checked for underflow on subtraction. All types are checked for division by zero.
Types are not implicitly converted for arithmetic operations and, if needed, must be explicitly coerced to the same types.
typedecl Foo = Nat;
1i + 2i //3i
3.5f + 2.5f //6.0f
2n - 1n //1n
2n - 3n //error Nat underflow
3.0f / 0.0f //error division by zero
2n_Foo + 1n_Foo //3n_Foo
2n_Foo * 3n_Foo //error Foo^2 is not well defined
2n_Foo * 3n //6n_Foo
Binary numeric comparison operators
Bosque supports the standard set of binary numeric comparison operators of ==, !=, <, <=, >, and >=. These are defined for all numeric types and automatically for any typedecl of a numeric type.
Types are not implicitly converted for comparison operations and, if needed, must be explicitly coerced to the same types.
typedecl Foo = Nat;
1i == 2i //false
1i != 2i //true
3.5f <= 2.5f //false
2/3R == 4/6R //true
1/3R < 1/2R //true
2n_Foo > 1n_Foo //true
2n_Foo !== 3n_Foo //true
Binary KeyType equality operators
KeyType values play a critical role in Bosque. They are used as keys in Set<T> and Map<K, V> containers and they are also used for strong equality testing with the === and !== operators. These operators allow for testing of values when (at least) one of the arguments is a KeyType value. The types on the two sides of the operator do not need to be the same but one must be a subtype of the other.
Examples of KeyType equality operator usage includes:
1i === 1i //true (identical to 1i == 1i)
"hello" !== "goodbye" //false
true === none //false
let x: Int | None = 1i;
x === none //false
x === 1i //true
x === 2i //false
let y: Option<String> = some("hello");
y === none //error types don't overlap
y === nothing //true
let p: String | Int = "hello";
let q: String | Int | None = "hello";
p === q //true
entity Foo {}
let f: Foo? = Foo{};
let g: Foo? = none;
f === g //error at least one type must be a KeyType
f === none //false
g === none //true
Binary Logic &&/||/==> operators
Bosque provides the standard short-circuiting boolean operators of && and ||. Bosque also has a logical implication operator ==> which is short-circuited on the left side when it is false.
true && true //true
true && false //false
false && (1i / 0i == 1i) //false -- short-circuited
true || false //true -- short-circuited
false || false //false
true ==> true //true
true ==> false //false
false ==> (1i / 0i == 1i) //true -- short-circuited
MapEntry Constructor => operator
The Map<K, V> container in Bosque holds values of type MapEntry<K, V>. The Map entry constructor => is a shorthand notation for creating MapEntry values from a key and value expression. The type of the entry is inferred from the context.
1i => 2i; //MapEntry<Int, Int>{1i, 2i}
Map<Int, String>{1i => "one", 2i => "two"}; //Map<Int, String>{MapEntry<Int, String>{1i, "one"}, MapEntry<Int, String>{2i, "two"}}
If-Then-Else Expression
Bosque supports if-then-elif*-else syntax for expressions. The type of the expression is the union of all the branch expressions (and type inference is applied to each expression if possible). The test conditions include the standard boolean expression form and any ITest expression! When using the ITest expression form it is also possible to use Binder syntax (the $ variable) in the branch expression which is bound to the value of the expression if the test passes.
Examples of simple if-then-else expressions:
if (x < 0i) then -x else x //Int
if (x == 0i) then 0i elif (x < 0i) then -1i else 1i //Int
let yy: Int? = if (x == 0i) then none else x //Inferred as Int? in each branch expression
Examples of if-then-else expressions with ITest expressions:
let x: {f: Int?, g: String} = ...;
if !none (x.f) then //special none ITest form
1i
else
0i
if <None> (x.f) then //typeof form ITest
0i
else
1i
if [none] (x.f) then //literal equality form ITest
0i
else
1i
Examples of if-then-else expressions with ITest expressions and binders:
let x: {f: Int?, g: String} = ...;
if !none (x.f) then
$ //$ is bound to x.f@!none in the branch expression
else
0i
if none (x.f) then
0i
else
$ //$ is bound to false flow (x.f@!none) in the else branch expression
Switch Expression
Bosque provides a switch expression for matching against a set of literal cases. The matches in a switch expression can be literals or the special _ wildcard match. As with if expressions the branch expressions are unioned to determine the type of the switch expression, type inference is applied if possible, and binders can be used in the branch expressions (bound to the switch expression and type refined according to matched/unmatched tests).
Non-exhaustiveness is not checked from the values but a runtime error will be raised if there is no matching case.
Examples of simple switch expressions include:
let x: Int? = ...;
switch (x) {
|none => 0i
| 0i => 1i
| _ => 2i
} //Int
let y: Bool = ...;
switch (y) {
| true => 0n
| false => 1n
} //Nat
let z: Int? = switch(x) {
| none => 0i
| _ => 1i
}; //Int?
Examples of switch expressions with binders include:
let x: {f: Nat?, g: Int} = ...;
switch (x.f) {
|none => 0n
| _ => $ + 1n
} //Int
let y: Option<Nat> = ...;
switch (y) {
| nothing => 0n
| _ => $.value() + 1n
} //Nat
Match Expression
The Bosque match expression is similar to the switch expression but uses type tests instead of literal tests. The match expression is a union of the branch expressions and type inference is applied if possible. Binders can be used in the branch expressions (bound to the match expression and type refined according to matched/unmatched tests).
Non-exhaustiveness is not checked from the values but a runtime error will be raised if there is no matching case.
Examples of simple match expressions include:
let x: Int | Nat | None = ...;
match (x) {
| None => 0i
| Int => 1i
| _ => 2i
} //Int
let z: Int? = match(x) {
| None => 0i
| Int => 1i
}; //Int?
Examples of match expressions with binders include:
let x: {f: Nat?, g: Int} = ...;
match (x.f) {
None => 0n
| _ => $ + 1n
} //Int
let y: Option<Nat> = ...;
match (y) {
Nothing => 0n
| Something<Nat> => $.value() + 1n
} //Nat
Bosque Expression Components
ITests
The Bosque language type checker uses a novel explicit flow-sensitive typing algorithm. Instead of relying on a set of heuristics and implicit rules in the checker logic Bosque makes the flow typing an explicit part of the language syntax. Inference introduction is limited to function/method arguments, let/var bindings and return statements where the type of the binding or return value is used to infer type of the expression (or vice a versa). These introductions are then pushed down the expression tree to the leaves or propagated up to roots. Otherwise flow and inference are explicitly stated with Itests and Binders.
There are three flavors of ITests in bosque:
- Type ITests - These are used to test if an expression is of a specific type in a classic subtyping sense. The syntax for these is
<Type>whereTypeis a type expression and!<Type>is an negated version of the test. - Value ITests - These are used to test if an expression is a specific value, which must be a literal comparable with the
===or!==operator.The syntax for these is[Literal]whereLiteralis a literal expression and![Literal]is a negated version that uses the!==semantics. - Special Constructor ITests - These are used to test if an expression is a specific special constructor and then, depending, on the context will also extract and bind values. The syntax for these is:
none- tests if the expression isnoneand converts the result to a nonesome- tests if the expression issomeand converts the result to a somenothing- tests if the expression isnothingand converts the result to a nothingsomething- tests if the expression issomethingand converts the result toTcorresponding to theSomething<T>type valueok- tests if the expression isokand converts the result toTcorresponding to theResult<T, E>::Oktype valueerr- tests if the expression iserrand converts the result toEcorresponding to theResult<T, E>::Errtype valueresult- tests if the expression isresultand converts the result toResult<U, V>corresponding to the contextual Result type value
[TODO] the result case has missing support in some of the positions.