Improvements to switch

Nick Treleaven nick at geany.org
Tue Apr 23 17:14:00 UTC 2024


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.
>>>> ```d
>>>> case :9 || :15 -> "not prime";
>>>> ```
>>>
>>> This is already covered by regular switch statements. You can 
>>> write:
>>> ```D
>>> 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:
```d
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.
>>>> ```d
>>>> switch (variant) {
>>>>     case int i {
>>>>         case if (i < 0) -> "negative int";
>>>>         default -> "some other int";
>>>>     }
>>>> ```
>>>
>>> ```D
>>> 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:
>
> ```D
> 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:
>
> ```D
> 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.



More information about the dip.ideas mailing list