[fine] Suggestion for improvement in match
No special keyword. Exhaustivity is harder.
This commit is contained in:
parent
f2e82942df
commit
93c9dcd13b
1 changed files with 22 additions and 19 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue