On Friday, 19 April 2024 at 19:27:22 UTC, Meta wrote:
>On Friday, 19 April 2024 at 07:40:34 UTC, Nick Treleaven wrote:
>On Friday, 19 April 2024 at 06:28:55 UTC, Meta wrote:
>On Wednesday, 17 April 2024 at 11:24:16 UTC, Nick Treleaven wrote:
>On Tuesday, 16 April 2024 at 18:25:45 UTC, Meta wrote:
>- branch guards
- pattern match on arrays, slices:
Some other things, based on section 3.3 of this C++ proposal:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2392r2.pdf
- Multiple alternatives that have the same result: ||
E.g.
case :9 || :15 -> "not prime";
This is already covered by regular switch statements. You can write:
case 9, 15 -> "not prime";
It would be ambiguous to write e.g. case name if (cond) ->
- is it matching a value name
, or is name
naming the switch variable?
I'm not quite sure what you mean - can you illustrate in more detail?
Where you had:
auto s = switch (input) {
case n if (n == 0) -> "zero";
case n if (n > 0) -> "greater than zero";
case n if (n < 0) -> "less than zero";
I understand that in the last case, it's giving input an alternative name n, then testing n < 0.
But suppose n is already a symbol in scope, and the test might not involve n:
case n if (p < 0)
I might expect that to match when input is equal to the value of n and p is negative. But how does the parser know if n is supposed to be an alternative name for input, or to be a value looked up in the current scope. It can be figured out at semantic time, but I think it would be better to design the syntax to be unambiguous at the parser stage about whether it is introducing a new symbol or not.
> >But the grammar was me trying to extrapolate from your examples, and it might not be workable for that to be compatible with today's switch statement. Perhaps it's better to not reuse switch
because we will want pattern matching with multiple statement branches, we won't always want switch
to be an expression.
Yeah maybe not. That was just some mock syntax off the top of my head, and it's probably not suitable for extracting a formal grammar.
> > >- Grouping common names and constraints: { }
E.g.
switch (variant) {
case int i {
case if (i < 0) -> "negative int";
default -> "some other int";
}
switch (variant) {
// goto default is already a feature of regular switch statements
case int i -> i < 0 ? "negative int" : goto default;
default -> "some other int";
}
This will work if goto default
is typed as noreturn
. I doubt that's the case, but that's something that can also be fixed in the compiler.
BTW that's not what my example does - the i < 0
is part of the matching, not part of the result. The difference is there can be other case
statements under the first one. case if (i < 0) ->
would try the next case statement when i >= 0 rather than jumping to the default case.
I see. I think having nested case conditions might make it too complex to understand and maybe even implement.
I think it would be straightforward, but indeed could wait until later.
> >Also for your example I don't understand why goto default
wouldn't have the same type as the result for the default
branch.
Conceptually, goto default
and other constructs that transfer execution to a different part of the code should be typed as noreturn
, because then you can do stuff like:
auto input = readln() || throw new Exception("empty input");
Although in this case it would actually be pretty weird... I think it would enable this type of code:
Variant v = 10;
auto str = switch (variant) {
case int i -> i < 0 ? "negative int" : goto default;
default -> writeln("invalid value");
};
// The only sane type for `str` is `noreturn`, and thus it should crash the program if we try to read from it.
I would expect the default case to produce a value unless it does not terminate. So I would require writing an assert(0) at the end of it.