Open adamchalmers opened 1 month ago
Moving @jtran 's comment here:
Would solution 2 support split range? I.e. a low range works and a high range works, but in the middle, it's an error.
let screwDiameter = selectFromRange(holeDiameter, "screw size", [
minimum 0.16,
up to 0.17 => 0.1875,
up to 0.18 => 0.1920,
up to 0.19 => 0.1900,
maximum 0.19
minimum 0.21,
up to 0.22 => 0.22,
maximum 0.22
])
Would solution 2 support variables, or do they need to be constant literals? For example, could I write this?
fn thing = (holeDiameter) => {
let a = 0.16
let increment = holeDiameter / 3
let screwDiameter = selectFromRange(holeDiameter, "screw size", [
minimum a,
up to a + 1 * increment => 0.1875,
up to a + 2 * increment => 0.1900,
up to a + 3 * increment => 0.1920,
maximum a + 4 * increment
])
}
Solution 2 feels very specialized to me. Based on the above, I'm wishing that there were some more primitive concepts that we could build solution 2 out of.
If it were a functional language, the primitives would be pattern matching combined with special underderstanding of how numbers work. You'd pattern match on holeDiameter
, and the implementation could detect that you've covered all the cases, possibly with a catch-all. Maybe we can add syntax sugar if this is common.
let screwDiameter = select(holeDiameter, "screw size", [
0.16 .. 0.17 => 0.1875,
.. 0.18 => 0.1920,
.. 0.19 => 0.1900,
])
In the above, to the left of =>
is any pattern, so the name selectFromRange
doesn't need to be a range anymore, even though we've used ranges here.
The above would be a "compile" error (or whatever phase we can check, ideally before runtime) because it's non-exhaustive.
let screwDiameter = select(holeDiameter, "screw size", [
0.16 .. 0.17 => 0.1875,
.. 0.18 => 0.1920,
.. 0.19 => 0.1900,
_ => error
])
This uses a catch-all to make the match exhaustive. In that case, it evaluates to an error. It's actually a value that select
recognizes and converts into a real runtime error with context of "screw size" and the runtime value of holeDiameter
.
These are all just half-baked ideas off the top of my head.
A different take: The Ultimate Conditional Syntax.
Tl;dr: They merge pattern matching with if-then-else, similar to Rust's if-let chains, but more concise.
First, read https://github.com/KittyCAD/modeling-app/issues/3677
The
if-else
chain in that issue has two problems:if
statement is brittle and makes it easy to cause logic errors. It's very sensitive to order, if the user accidentally puts the 0.17 block first, it'll trigger and stop the 0.18 or 0.19 blocks from being evaluated. Or if they get the > wrong and put < instead it'll all get messed up. These errors are hard to notice and unless the user exhaustively tests all possible values, they won't notice the mistake.So we should consider a dedicated syntax for this, as it's likely to be a common pattern. One hypothetical example syntax:
the KCL executor would check that all the selections are in the right order, and that all possible values are covered. It forces users to explicitly consider the minimum/maximum ranges they support. It'd output a nicely-formatted error if the user goes above/below the range, e.g. if you put in 0.11 it'd error with
We'd need some way to distinguish <= and <, e.g. "up to 0.18" vs. "up to but not 0.18"