Open simonrouse9461 opened 3 years ago
@simonrouse9461, I consider your example that matches "2" twice an anti-pattern. The Go language team deliberately decided not to follow the "C" language switch fall-through behavior. I would be extremely unhappy if Elvish behaved like the "C" language in this regard. Also, this can't be done via "builtins". It must be part of the syntax of the language.
The Go language team deliberately decided not to follow the "C" language switch fall-through behavior. I would be extremely unhappy if Elvish behaved like the "C" language in this regard.
I see, but whether to allow fall-through is not my point here. If fall-through should be avoided, adding a break
will fix it
fn switch [value case_fns @_else]{
var matched = $false
$case_fns | each [case_fn]{
try {
$case_fn $value
} except {
continue
} else {
set matched = $true
break
}
}
if (and (not $matched) (> (count $_else) 0) (eq $_else[0] else)) {
$_else[1]
}
}
Also, this can't be done via "builtins". It must be part of the syntax of the language.
Sorry if my words are misleading, but I don't actually mean it has to be builtins. I'm just suggesting to have this type of syntax emulated by these two functions regardless of how it is implemented under the hood.
@simonrouse9461 I like your trick of implementing switch
and case
purely with function semantics :)
I also like how clean the syntax feels, but I want to avoid having two levels of indentation. The fact that the "normal" cases are indented differently also feels a bit awkward.
Another goal I have for switch
is allowing specifying an alternative the matching function (the default can be eq
). For example, ==
is useful when switch
ing on a number, and re:match is useful when switch
ing on a string. In the syntax you proposed this can be done by adding either an optional argument or a named option to switch
.
Hmm, if we (1) simply don't indenting the case
clauses and (2) put the else
clause inside the block, that would fix my complaints, but it looks weird, especially for the double closing }
at the same level:
switch 2 {
case 2 3 5 {
echo "small prime number"
}
case 2 4 6 {
echo "small even number"
}
else {
echo "something else"
}
}
How about something like this:
switch 2 case 2 3 5 {
echo "small prime number"
} case 2 4 6 {
echo "small even number"
} else {
echo "something else"
}
and with explicit matching function:
switch 2 [pattern value]{
eq $pattern $value
} case 2 3 5 {
echo "small prime number"
} case 2 4 6 {
echo "small even number"
} else {
echo "something else"
}
I know it's still a little weird, since the first case
doesn't align with the rest in the first example. Or one can just use a ^
to force case
to a new line?
switch 2 $re:match~ ^
case '.*'{1 3 5 7 9} {
echo "odd number"
} case '.*'{2 4 6 8 0} {
echo "even number"
} else {
echo "something else"
}
For reference, I wrote an util:cond
function which allows matching against a number of conditions in sequence. If a new builtin is added, maybe this would be a more generic way to do it?
See my implementation at https://github.com/zzamboni/elvish-modules/blob/master/util.org#conditionals
How about something like this:
switch 2 case 2 3 5 { echo "small prime number" } case 2 4 6 { echo "small even number" } else { echo "something else" }
and with explicit matching function:
switch 2 [pattern value]{ eq $pattern $value } case 2 3 5 { echo "small prime number" } case 2 4 6 { echo "small even number" } else { echo "something else" }
I know it's still a little weird, since the first
case
doesn't align with the rest in the first example. Or one can just use a^
to forcecase
to a new line?switch 2 $re:match~ ^ case '.*'{1 3 5 7 9} { echo "odd number" } case '.*'{2 4 6 8 0} { echo "even number" } else { echo "something else" }
TBH I think they all look a bit weird... My current thinking is to just accept two levels of indentation.
The two-level syntax is kind of similar to Perl's given/when
, and I'm ambivalent about whether that's an upside or downside :) (For those familiar with Perl, Elvish doesn't have the concept of topic variable and I don't intend to introduce it, so the resemblance is mostly superficial.)
For reference, I wrote an
util:cond
function which allows matching against a number of conditions in sequence. If a new builtin is added, maybe this would be a more generic way to do it?See my implementation at https://github.com/zzamboni/elvish-modules/blob/master/util.org#conditionals
Nice :)
Clojure's expression-centered syntax style doesn't fit well with the rest of Elvish though, so I'll prefer the currently proposed switch/case syntax.
If I may express my opinion, I like the syntax from @simonrouse9461 the best. I'm not sure whether it'd cause any parsing ambiguity issues, but I find it to be the least cluttered solution and the best for conveying the intention.
If I may express my opinion, I like the syntax from @simonrouse9461 the best. I'm not sure whether it'd cause any parsing ambiguity issues, but I find it to be the least cluttered solution and the best for conveying the intention.
Thanks! I'm glad you like it. I feel one import merit of this syntax is that the structure is "flat", as it can be unrolled into a single line without messing up the readability, which makes it more suitable for being used as an interactive shell command.
One problem of using a two-level structure is that it introduces a syntactical exception, where the code block accepted by the switch command cannot be a general lambda type, which obfuscates the syntactical role of curly braces. I worry that this may contaminate the clean and unified syntax as it currently is.
Elvish doesn't currently support switch-case control flow, which in a lot of cases can be much more readable than if-else syntax.
One can simply emulate switch-case syntax by defining two functions like this:
Then, this becomes possible:
which will output,
This kind of syntax can be much cleaner and readable in certain cases than vanilla if-else. So, can we have two builtins
switch
andcase
to achieve similar syntax?