Matching same enum twice seems to duplicate the default case.
import haxe.ds.Option;
class Test {
static function main() {
var a = [
Live,
Offline({ vct: None, ot: Some('foo') }),
Offline({ vct: Some('bar'), ot: None }),
];
switch a[Std.random(a.length)] {
case Offline({ vct: Some(v) }): trace('1 $v');
case Offline({ ot: Some(v) }): trace('2 $v');
case _:
trace("default!");
}
}
}
enum A {
Offline(v:{ vct:Option<String>, ot:Option<String> });
Live;
}
You'll find the code generated for trace("default!"") twice in the output, both with Haxe 4 and nighly.
Yes that can happen. Match defaults are one of the few ways to get the compiler to duplicate code. I could think of ways to improve this on targets that have native goto, but on targets like JS it's more difficult. I think a coroutine-like transformation could work here, but that comes with a lot of complexity and not a lot of readability, so I don't know.
The way pattern matching is "unrolled", if there was not twice trace("default!"") , pattern like Offline({ vct :None, ot : None }) wouldn't be catched.
BTW I noticed a similar thing when catching exceptions (custom exceptions that extends haxe.Exception) and "final" haxe.Exception ... Idk if it's clear, if not, forget that lol
An example of what I wanted to say : https://try.haxe.org/#E6C1dd45 It seems similar to me, but maybe I'm wrong
Yes that can happen. Match defaults are one of the few ways to get the compiler to duplicate code. I could think of ways to improve this on targets that have native
goto, but on targets like JS it's more difficult. I think a coroutine-like transformation could work here, but that comes with a lot of complexity and not a lot of readability, so I don't know.
Hmm, could the default be moved into a local function, at least if it exceeds some "size" (whatever the heuristic for that may be)? Since the above example is minimal(ish), it's not so apparent what the real problem is: if one uses a default case that does contain a lot of code, it's potentially emitted a whole number of times, substantially inflating output size.
Or if this is truly about the default case alone, then I wonder if wrapping the switch in a self executing anonymous function (ideally an arrow function for ES6 output, to avoid the this capture dance) would do the trick, so one can jump out with early returns and leave the default at the end.
I thought about solutions like that and I agree they would cover most cases, but using local functions like that brings new problems too. Any actual early return has to be dealt with, and for switches in loops there's also break and continue to consider.
I think a general solution would need a function that (conceptually) returns some state enum like this:
enum R {
Normal(value);
Return(value);
Break;
Continue;
}
That has its own set of problems as well, one of which being that we would have to duplicate the evaluation code at each call-site, which almost defeats the purpose. And if we try to isolate that away further we're getting pretty close to a coroutine-style state machine.
Ah, right, switches in loops, yeah, I can see how that complicates things ^^