See embedded comments On Thu, Oct 15, 2020 at 4:58 PM Fred Hebert <[email protected]> wrote:
> > On Thu, Oct 15, 2020 at 3:31 AM Kenneth Lundin <[email protected]> wrote: > >> We welcome initiatives like this and are positive to revisit this. >> A proposal for something closer to *with* in Elixir looks interesting. >> > > Alright. So before I get into the big details, here are a few things / > variables I'm considering if we are to redesign this. > > I'm labelling them as below into proposal rewrites 1 through 3, some with > variants. Let me know if some of them sound more interesting. > > > > First, dropping the normative return values of ok | {ok, T} | {error, R}: > > begin > {ok, A} <~ exp(), > {ok, B} <~ exp(), > {ok, A+B} > end. > > This has interesting impacts in some of the examples given in the EEP, > specifically in that _ <~ RHS now means as much as _ = RHS, but also that > it allows rewriting some forms. The RFC looked at expressions such as: > > backup_releases(Dir, NewReleases, Masters, Backup, Change, RelFile) -> > case at_all_masters(Masters, ?MODULE, do_copy_files, [RefFile, > [Backup, Change]]) of > ok -> > ok; > {error, {Master, R}} -> > remove_files(Master, [Backup, Change], Masters) > end. > > Could now be written the following way: > > backup_releases(Dir, NewReleases, Masters, Backup, Change, RelFile) -> > begin > {error, {Master, R}} <~ at_all_masters(Masters, ?MODULE, > do_copy_files, [RefFile, [Backup, Change]]), > remove_files(Master, [Backup, Change], Masters) > end. > > Which looks a bit funny because the main path is now the error path and > the happy-path is fully removed from the situation. > > In any case, this is the most minimal rework required, has some edge cases > pointed out by the EEP already (a match for {ok, [_|_]=L} <~ RHS can end > up doing a short return for {ok, Tuple} for example, and interfere with > expected values). I'll call this *proposal rewrite 1. * > > We can't avoid the above escaping mechanism without normalizing over what > is an acceptable or unacceptable good match value, and proposal rewrite 1 > makes this impossible. This requires adding something akin to the else > construct in the elixir with: > > %% because of `f()', this returns `{ok, "hello"}' instead of `{ok, > <<"Hello">>}' > %% or a badmatch error. > f() -> {ok, "hello"}. > validate(IoData) -> size(IoData) > 0. > sanitize(IoData) -> string:uppercase(IoData). > > fetch() -> > begin > {ok, B = <<_/binary>>} <~ f(), > ok <~ validate(B), > {ok, sanitize(B)} > end. > > %% Only workaround: > fetch_workaround() -> > begin > {ok, B = <<_/binary>>} <~ f(), > ok <~ validate(B), > {ok, sanitize(B)} > else > {ok, [_|_]} -> ... > end. > > This format might work, but requires introducing new extensions to the begin > ... end form (or new things like maybe ... end), regardless of terms. In > terms of semantics, a catch, of, or after block might reuse existing > keywords but wouldn't be as clear in terms of meaning. Specifically > addressing this requirement that comes from relaxing semantics for proposal > 1 is *proposal rewrite 2*. *Variant A* would be to keep it as described > above, and *Variant B* would include the potential options with other > alternative keywords and blocks. > > Either way, dropping the pattern and changing constructs maintains the > overall form and patterns described in the EEP. They however still keep LHS > <~ RHS as a special expression type that is always contextual, which was > pointed out to be a thing the OTP team did not like. Making it apply > everywhere is a particularly tricky bit, but I think it might be possible. > > First, we need to define where a free-standing LHS <~ RHS is going to > return. If it's free-standing it can't be a sort of macro trick for a case > expression, and it can't also be based on a throw, since throws can't > clearly disambiguate the control flow required for this construct vs. > random exceptions people could be handling at lower levels.I've seen in > EEP-52 that there is a core-erlang construct as a letrec_goto, and using it > we might be able to work with that. > > We'd first have to choose which scope nested expressions would need to > return to: > > a() -> > V = case f() of > true -> > ok <~ g(), > h(); > false -> > {ok, X} <~ i(), > k(element(2, {ok, Y} <~ j(X))) > end, > handle(V). > > This is an interesting test bed for some possible execution locations > where the new operator could be bound. We could pick: > > - shortcut the lexical scope: since case expressions and any other > construct share the parent lexical scope and can export variables, we would > have to expect that {ok, X} <~ i() implies that a() itself can return > i()'s value directly if it doesn't strictly match the form, regardless > of how deeply nested we are in the conditional. This is unlikely to be > practical or expected to people, but would nest appropriately within funs. > It may have very funny effects on list comprehensions when used as part of > generators and that likely will need special treatment. > - shortcut to the parent control flow construct / end of current > sequence of expression: I don't know how to word this properly, but the > idea would be to limit the short-circuit return to the prior branching or > return point in the language. This means that {ok, X} <~ i() failing > implies that V gets bound to the return value of i(), and similarly > for the return value of j() if it were to fail. Upon seeing a LHS <~ > RHS expression, the compiler would need to insert a label at the end > of the current sequence of expressions (which may conveniently going to be > explained as "all of the current expressions separated by a comma [,]), > and do a conditional jump to it if the expression fails to match. If it > works it keeps chugging along, and the last expression in the sequence can > just jump to the same label with the identified return value. To my > understanding, this wouldn't interfere with LCO nor require more > stackframes than any other conditional would ever require. > > I think the middle bullet (above this) is the most interesting. The LHS <~ RHS expression should be thought of as MATCH_OR_BREAK or MATCH_OR_RETURN a conditional return as I think you mention somewhere. The scope to break out from is function clause, begin/end, case clause and probably something else which I have not thought of yet. If we introduce this maybe it is strange to not introduce an unconditional return as well. Will take a closer look into the 'with' construct in Elixir and see if there is anything more we could copy. Note, I have discussed this briefly with some of the OTP team members, see this as an initial view point not written in stone. > > - Something else I haven't thought of > > I also assume that none of these expressions would ever be valid in guards > since they can't do assignment today. I'm also unsure of whether > letrec_goto can use a label with arguments in what to execute (which would > let it carry/return a variable), but I'm waiting to do research on that on > whether this idea looks good or not to the OTP team. I think the lexical > scope option is unacceptable. I call the sequence of expressions approach > *proposal > rewrite 3*. This is much more ambitious and could have a ton weirder > unexpected effects, but it drops all pretense and introduces a new > operation type/control flow mechanism (which is comparable to a conditional > return somewhat scoped like a continue in an imperative language) rather > than a new operator within a bound construct. > > An interesting *Variant B* for this one would be that since we make the > expression general, we could change the LHS <~ RHS expression to instead > be LHS <- RHS expression; after all, there is no unwrap involved anymore, > and the logical handling of this thing is now much closer to what you'd see > in a list comprehension such as [handle(X) || {ok, X} <- [i()]]. > > Let me know what you think about these. > /Kenneth, Erlang/OTP Ericsson > _______________________________________________ > eeps mailing list > [email protected] > http://erlang.org/mailman/listinfo/eeps >
_______________________________________________ eeps mailing list [email protected] http://erlang.org/mailman/listinfo/eeps
