152 lines
No EOL
3.5 KiB
Text
152 lines
No EOL
3.5 KiB
Text
// Examples of alternate types/union types/heterogeneous types.
|
|
class Foo {
|
|
x: f64;
|
|
}
|
|
|
|
class Bar {
|
|
y: f64;
|
|
}
|
|
|
|
fun extract_value(v: Foo or Bar) -> f64 {
|
|
match v {
|
|
v:Foo -> v.x,
|
|
v:Bar -> v.y,
|
|
} // No error; exhaustivity analysis should work.
|
|
}
|
|
|
|
// Some type compatibility nonsense
|
|
fun bottom() -> string or nothing {
|
|
"woooo"
|
|
}
|
|
|
|
fun same_but_wrong_way_around_should_be_compatible() -> nothing or string {
|
|
bottom()
|
|
}
|
|
|
|
fun same_but_larger_should_be_compatible() -> nothing or f64 or string {
|
|
bottom()
|
|
}
|
|
|
|
|
|
// This is Bob Nystrom's example from
|
|
// https://journal.stuffwithstuff.com/2023/08/04/representing-heterogeneous-data/
|
|
//
|
|
class MeleeWeapon {
|
|
damage: f64;
|
|
}
|
|
|
|
class RangedWeapon {
|
|
minRange: f64;
|
|
maxRange: f64;
|
|
}
|
|
|
|
class Monster {
|
|
health: f64;
|
|
}
|
|
|
|
fun print(x:string) {}
|
|
|
|
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
|
|
match weapon {
|
|
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
|
|
_ -> distance == 1
|
|
}
|
|
}
|
|
|
|
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
|
|
// This is worse than Bob's final version but but it works. `is` operator
|
|
// should be the same precedence as `and` and left-associative, so the
|
|
// `and` that follows the declaration in the `is` still has the variables
|
|
// from the `is` binding in scope.
|
|
if weapon is MeleeWeapon and distance > 1 or
|
|
weapon is w:RangedWeapon and (distance < w.minRange or distance > w.maxRange) {
|
|
print("You are out of range");
|
|
return
|
|
}
|
|
|
|
// Bob says he doesn't want to do flow analysis and we're not, we're using
|
|
// scoping rules and associativity to make the re-bindings work.
|
|
//
|
|
// NOTE: special syntax here: `identifier` is `TypeExpression` ALMOST means
|
|
// `identifier is identifier : TypeExpression` as the shorthand for checking
|
|
// local variables. The *almost* part is that the effective type of the
|
|
// variable changes but not the binding. (Is this what we want?)
|
|
//
|
|
let damage = match weapon {
|
|
w:MeleeWeapon -> roll_dice(w.damage),
|
|
w:RangedWeapon -> w.maxRange - w.minRange,
|
|
};
|
|
|
|
if monster.health <= damage {
|
|
print("You kill the monster!");
|
|
monster.health = 0
|
|
} else {
|
|
print("You wound the monster.");
|
|
monster.health = monster.health - damage
|
|
}
|
|
}
|
|
|
|
fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or nothing {
|
|
if weapon is w: RangedWeapon and w.maxRange > 10 {
|
|
// w is still in scope here; the `and` is bound into a predicate expression
|
|
// and breaks exhaustivity
|
|
w.minRange
|
|
}
|
|
}
|
|
|
|
// Some fun with iterators
|
|
class Finished {}
|
|
let FINISHED = new Finished {};
|
|
|
|
class Iterator {
|
|
current: f64;
|
|
|
|
fun next(self) -> f64 or Finished {
|
|
if self.current < 10 {
|
|
let result = self.current;
|
|
self.current = self.current + 1;
|
|
return result;
|
|
}
|
|
|
|
FINISHED
|
|
}
|
|
}
|
|
|
|
fun test() -> f64 {
|
|
let sum = 0;
|
|
|
|
// A single step of an iterator...
|
|
let it = new Iterator { current: 0 };
|
|
if it.next() is v: f64 {
|
|
sum = sum + v;
|
|
}
|
|
|
|
// Looping by hand over an iterator is pretty clean.
|
|
let it = new Iterator { current: 0 };
|
|
while it.next() is v: f64 {
|
|
sum = sum + v;
|
|
}
|
|
|
|
let it = new Iterator { current: 0 };
|
|
sum = sum + match it.next() {
|
|
v:f64 -> 100,
|
|
_ -> 1000,
|
|
};
|
|
|
|
// Unroll by hand...
|
|
let it = new Iterator { current: 0 };
|
|
while true {
|
|
if it.next() is v: f64 {
|
|
sum = sum + v;
|
|
} else {
|
|
return sum;
|
|
}
|
|
}
|
|
|
|
// Not in this test but `for` over an object should turn into something
|
|
// like the above.
|
|
}
|
|
|
|
// @ignore not finished yet, still compiler bugs
|
|
// @no-errors
|
|
// @eval: Float(90.0) |