[fine] Suggestion for improvement in match

No special keyword. Exhaustivity is harder.
This commit is contained in:
John Doty 2024-01-30 06:39:10 -08:00
parent f2e82942df
commit 93c9dcd13b

View file

@ -8,9 +8,9 @@ class Bar {
} }
fun extract_value(v: Foo or Bar) -> f64 { fun extract_value(v: Foo or Bar) -> f64 {
if match v as Foo { if v is Foo {
v.x // Magic type binding! v.x // Magic type binding!
} else as Bar { } else if v is Bar {
v.y // Same magic re-binding of the variable to the new type v.y // Same magic re-binding of the variable to the new type
} // No error; exhaustivity analysis should work. } // No error; exhaustivity analysis should work.
} }
@ -34,7 +34,7 @@ class Monster {
fun print(x:string) {} fun print(x:string) {}
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) { fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
if match weapon as w : RangedWeapon { if weapon is w : RangedWeapon {
distance >= w.minRange and distance <= w.maxRange distance >= w.minRange and distance <= w.maxRange
} else { } else {
distance == 1 distance == 1
@ -42,26 +42,29 @@ fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
} }
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) { fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
// This is worse but it works. // This is worse than Bob's final version but but it works. `is` operator
if match weapon as MeleeWeapon and distance > 1 or // should be the same precedence as `and` and left-associative, so the
match weapon as w:RangedWeapon and (distance < w.minRange or distance > w.maxRange) { // `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") print("You are out of range")
return return
} }
// Bob says he doesn't want to do flow analysis but we're doing all our // Bob says he doesn't want to do flow analysis and we're not, we're using
// tricks with variables and scoping here, with one *big* bit of magic which // scoping rules and associativity to make the re-bindings work.
// is...
// //
// NOTE: special syntax here: `identifier` as `TypeExpression` ALMOST means // NOTE: special syntax here: `identifier` is `TypeExpression` ALMOST means
// `identifier as identifier : TypeExpression` as the shorthand for checking // `identifier is identifier : TypeExpression` as the shorthand for checking
// local variables. The *almost* part is that the effective type of the // local variables. The *almost* part is that the effective type of the
// variable changes but not the binding. (Is this what we want?) // variable changes but not the binding. (Is this what we want?)
let damage = if match weapon as w: MeleeWeapon { //
// TODO: What do we do about captured variables?
//
let damage = if weapon is w: MeleeWeapon {
roll_dice(w.damage) roll_dice(w.damage)
} else as w: RangedWeapon { } else if weapon is w: RangedWeapon {
// This is the trick here: else as re-uses the expression from the match
// and so we can do exhaustivity analysis.
w.maxRange - w.minRange w.maxRange - w.minRange
}; };
@ -75,7 +78,7 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
} }
fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () { fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () {
if match weapon as w: RangedWeapon and w.maxRange > 10 { if weapon is w: RangedWeapon and w.maxRange > 10 {
// w is still in scope here; the `and` is bound into a predicate expression // w is still in scope here; the `and` is bound into a predicate expression
// and breaks exhaustivity // and breaks exhaustivity
w.minRange w.minRange
@ -105,20 +108,20 @@ fun test() -> f64 {
// A single step of an iterator... // A single step of an iterator...
let it = new Iterator { current: 0 }; let it = new Iterator { current: 0 };
if match it.next() as v: f64 { if it.next() is v: f64 {
sum = sum + v; sum = sum + v;
} }
// Looping by hand over an iterator is pretty clean. // Looping by hand over an iterator is pretty clean.
let it = new Iterator { current: 0 }; let it = new Iterator { current: 0 };
while match it.next() as v: f64 { while it.next() is v: f64 {
sum = sum + v; sum = sum + v;
} }
// Unroll by hand... // Unroll by hand...
let it = new Iterator { current: 0 }; let it = new Iterator { current: 0 };
loop { loop {
if match it.next() as v: f64 { if it.next() is v: f64 {
sum = sum + v; sum = sum + v;
} else { } else {
return sum; return sum;