Re: [Erlang Forums] [Erlang/OTP Proposals/Proposals: RFC] Re-visiting EEP-0055

"Richard O'Keefe" <[email protected]> Fri, 29 Apr 2022 00:00:39 +1200
Newsgroups gmane.comp.lang.erlang.general
Message-ID <CABcYAdJJOs95o6YKcodewZKixCzZYo2wwZ9FEoiqq7pq0Ekjsw@mail.gmail.com>
--00000000000070ad3005ddb5ad53
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

I can't recall everything I said against this EEP,
but there are two issues I can think of right now,
and a better alternative.
(A) Inconsistency.
    Suppose a variable occurs multiple times
    in a single pattern.  What does it mean if
    it is "pinned" in some of those occurrences
    but not others?  Are there two different
    variables?
    Suppose a construct has multiple clauses,
    and a variable is "pinned" in some of those
    clauses but not others.  Are there two
    different variables?
(2) There are other problems with scope that we
    want to address, but "pinning" does nothing
    for them.  (The classic one is people
    expecting variables to be local to a 'case'
    but leaking out.)

The counter-proposal is quite simple.
(A) Fix the original problem.  Erlang had a very
    simple scope rule: the scope of a variable is
    the *entire* top-level function clause it
    occurs in.  Reinstate that rule.
    This is a breaking change, so it needs some
    sort of -simple_scopes declaration to enable it.
    We now have a feature declaration that can be
    used for this.

(B) Fix the locality problem.  The right way.
    By declaring which variables are *local*,
    not which variables are *not* local.
    Borrowing some syntax from Prolog (as Erlang
    originally got its syntax thence), let D be
    a syntactic form made of tuples, lists, and
    variables, and C be a pattern or expression.
    Then D ^ C means that each of the variables
    in D represents a new variable in C, not
    provided, visible, or in any way accessible
    in C.

With this approach, local scope is available for
*every* construct.  With the scope marker being
located at the *beginning* of a construct, instead
of being buried possibly deeply inside, it should
be easier to read.




On Thu, 28 Apr 2022 at 14:52, Dmytro Lytovchenko <
[email protected]> wrote:

> Would it provide a solution to just assume that the variable shadowing is
> never wanted?
>
> What if code like:
>     f(X) -> fun(X) -> X end.
>     g(Y) -> case h() of Y -> %code% end.
>
> 1. Could create an error due to X being shadowed.
> 2. Could compile with inner X shadowed but a guard is added by the Erlang
> compiler, comparing inner to the outer:
>     f(X) -> fun(X2) when X2 =3D:=3D X -> X2 end.
>     g(Y) -> case h() of Y2 when Y2 =3D:=3D Y -> %code% end.
> 3. Current behaviour: Inner X is shadowed and is an entirely new variable
> (leads to naming and scoping confusion)
>
> Solution 2 does not introduce new syntax and matches expectations for man=
y
> developers. The compiler fixes the confusion during the compile time. Als=
o
> should not break the existing code.
>
> On Wed, 27 Apr 2022 at 21:11, Attila Rajmund Nohl <[email protected]=
m>
> wrote:
>
>> Fred Dushin <[email protected]> ezt =C3=ADrta (id=C5=91pont: 2022. =C3=A1p=
r. 27., Sze,
>> 15:14):
>> >
>> [...]
>> > But as noted later in the EEP, this can be written as
>> >
>> > f(X, Y) ->
>> >     F =3D fun ({a, Z}) when Z =3D:=3D Y  -> {ok, Y};
>> >             (_) -> error
>> >         end,
>> >     F(X).
>> [...]
>> > And not to be too critical, but I am having a hard time understanding
>> parts of the Rationale section.  The author(s) suggest(s) that in curren=
t
>> Erlang temporary variables are needed to achieve the same as the propose=
d
>> glyph, but then provide an example that uses a guard (as above) but does=
n't
>> use a temporary variable, after all (?)
>>
>> Z is the temporary variable in the above example.
>>
>> > I am not sure what the problem is with temporary variables.  The
>> compiler has registers at its disposal, so I don't think it's a performa=
nce
>> argument, but more an issue of readability, which again, I think is a
>> purely aesthetic question, and has no bearing on the features of the
>> language, per se.  In other words, the proposal is not suggesting anythi=
ng
>> that cannot already be achieved in current Erlang, with effectively the
>> same compiled BEAM ASM.
>>
>> Naming is hard, and if you have to come up with two meaningful
>> variable names instead of one, it's even harder. This leads to code
>> like this:
>>
>>      F =3D fun ({a, TmpMeaningfulName}) when TmpMeaningfulName =3D:=3D
>> MeaningfulName  -> {ok, MeaningfulName};
>>
>> The current solution in the language easily leads to too long lines or
>> overly verbose code.
>>
>

--00000000000070ad3005ddb5ad53
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div class=3D"gmail_default" style=3D"font-family:monospac=
e,monospace">I can&#39;t recall everything I said against this EEP,</div><d=
iv class=3D"gmail_default" style=3D"font-family:monospace,monospace">but th=
ere are two issues I can think of right now,</div><div class=3D"gmail_defau=
lt" style=3D"font-family:monospace,monospace">and a better alternative.</di=
v><div class=3D"gmail_default" style=3D"font-family:monospace,monospace">(A=
) Inconsistency.</div><div class=3D"gmail_default" style=3D"font-family:mon=
ospace,monospace">=C2=A0=C2=A0=C2=A0 Suppose a variable occurs multiple tim=
es</div><div class=3D"gmail_default" style=3D"font-family:monospace,monospa=
ce">=C2=A0=C2=A0=C2=A0 in a single pattern.=C2=A0 What does it mean if</div=
><div class=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=
=A0=C2=A0=C2=A0 it is &quot;pinned&quot; in some of those occurrences</div>=
<div class=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=
=A0=C2=A0=C2=A0 but not others?=C2=A0 Are there two different</div><div cla=
ss=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=A0=C2=A0=
=C2=A0 variables?</div><div class=3D"gmail_default" style=3D"font-family:mo=
nospace,monospace">=C2=A0=C2=A0=C2=A0 Suppose a construct has multiple clau=
ses,</div><div class=3D"gmail_default" style=3D"font-family:monospace,monos=
pace">=C2=A0=C2=A0=C2=A0 and a variable is &quot;pinned&quot; in some of th=
ose</div><div class=3D"gmail_default" style=3D"font-family:monospace,monosp=
ace">=C2=A0=C2=A0=C2=A0 clauses but not others.=C2=A0 Are there two</div><d=
iv class=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=A0=
=C2=A0=C2=A0 different variables?</div><div class=3D"gmail_default" style=
=3D"font-family:monospace,monospace">(2) There are other problems with scop=
e that we</div><div class=3D"gmail_default" style=3D"font-family:monospace,=
monospace">=C2=A0=C2=A0=C2=A0 want to address, but &quot;pinning&quot; does=
 nothing</div><div class=3D"gmail_default" style=3D"font-family:monospace,m=
onospace">=C2=A0=C2=A0=C2=A0 for them.=C2=A0 (The classic one is people</di=
v><div class=3D"gmail_default" style=3D"font-family:monospace,monospace">=
=C2=A0=C2=A0=C2=A0 expecting variables to be local to a &#39;case&#39;</div=
><div class=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=
=A0=C2=A0=C2=A0 but leaking out.)</div><div class=3D"gmail_default" style=
=3D"font-family:monospace,monospace"><br></div><div class=3D"gmail_default"=
 style=3D"font-family:monospace,monospace">The counter-proposal is quite si=
mple.</div><div class=3D"gmail_default" style=3D"font-family:monospace,mono=
space">(A) Fix the original problem.=C2=A0 Erlang had a very</div><div clas=
s=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=A0=C2=A0=
=C2=A0 simple scope rule: the scope of a variable is</div><div class=3D"gma=
il_default" style=3D"font-family:monospace,monospace">=C2=A0=C2=A0=C2=A0 th=
e *entire* top-level function clause it</div><div class=3D"gmail_default" s=
tyle=3D"font-family:monospace,monospace">=C2=A0=C2=A0=C2=A0 occurs in.=C2=
=A0 Reinstate that rule.</div><div class=3D"gmail_default" style=3D"font-fa=
mily:monospace,monospace">=C2=A0=C2=A0=C2=A0 This is a breaking change, so =
it needs some</div><div class=3D"gmail_default" style=3D"font-family:monosp=
ace,monospace">=C2=A0=C2=A0=C2=A0 sort of -simple_scopes declaration to ena=
ble it.</div><div class=3D"gmail_default" style=3D"font-family:monospace,mo=
nospace">=C2=A0=C2=A0=C2=A0 We now have a feature declaration that can be</=
div><div class=3D"gmail_default" style=3D"font-family:monospace,monospace">=
=C2=A0=C2=A0=C2=A0 used for this.</div><div class=3D"gmail_default" style=
=3D"font-family:monospace,monospace"><br></div><div class=3D"gmail_default"=
 style=3D"font-family:monospace,monospace">(B) Fix the locality problem.=C2=
=A0 The right way.</div><div class=3D"gmail_default" style=3D"font-family:m=
onospace,monospace">=C2=A0=C2=A0=C2=A0 By declaring which variables are *lo=
cal*,</div><div class=3D"gmail_default" style=3D"font-family:monospace,mono=
space">=C2=A0=C2=A0=C2=A0 not which variables are *not* local.</div><div cl=
ass=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=A0=C2=
=A0=C2=A0 Borrowing some syntax from Prolog (as Erlang</div><div class=3D"g=
mail_default" style=3D"font-family:monospace,monospace">=C2=A0=C2=A0=C2=A0 =
originally got its syntax thence), let D be<br></div><div class=3D"gmail_de=
fault" style=3D"font-family:monospace,monospace">=C2=A0=C2=A0=C2=A0 a synta=
ctic form made of tuples, lists, and</div><div class=3D"gmail_default" styl=
e=3D"font-family:monospace,monospace">=C2=A0=C2=A0=C2=A0 variables, and C b=
e a pattern or expression.</div><div class=3D"gmail_default" style=3D"font-=
family:monospace,monospace">=C2=A0=C2=A0=C2=A0 Then D ^ C means that each o=
f the variables</div><div class=3D"gmail_default" style=3D"font-family:mono=
space,monospace">=C2=A0=C2=A0=C2=A0 in D represents a new variable in C, no=
t</div><div class=3D"gmail_default" style=3D"font-family:monospace,monospac=
e">=C2=A0=C2=A0=C2=A0 provided, visible, or in any way accessible</div><div=
 class=3D"gmail_default" style=3D"font-family:monospace,monospace">=C2=A0=
=C2=A0=C2=A0 in C.</div><div class=3D"gmail_default" style=3D"font-family:m=
onospace,monospace"><br></div><div class=3D"gmail_default" style=3D"font-fa=
mily:monospace,monospace">With this approach, local scope is available for<=
/div><div class=3D"gmail_default" style=3D"font-family:monospace,monospace"=
>*every* construct.=C2=A0 With the scope marker being</div><div class=3D"gm=
ail_default" style=3D"font-family:monospace,monospace">located at the *begi=
nning* of a construct, instead</div><div class=3D"gmail_default" style=3D"f=
ont-family:monospace,monospace">of being buried possibly deeply inside, it =
should</div><div class=3D"gmail_default" style=3D"font-family:monospace,mon=
ospace">be easier to read.</div><div class=3D"gmail_default" style=3D"font-=
family:monospace,monospace"><br></div><div class=3D"gmail_default" style=3D=
"font-family:monospace,monospace"><br></div><div class=3D"gmail_default" st=
yle=3D"font-family:monospace,monospace"><br></div></div><br><div class=3D"g=
mail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Thu, 28 Apr 2022 at 14=
:52, Dmytro Lytovchenko &lt;<a href=3D"mailto:[email protected]"=
>[email protected]</a>&gt; wrote:<br></div><blockquote class=3D"=
gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(20=
4,204,204);padding-left:1ex"><div dir=3D"ltr"><div>Would=20
it provide a solution

to just assume that the variable shadowing is never wanted?</div><div><br><=
/div><div>What if code like:<br></div><div>=C2=A0=C2=A0=C2=A0 f(X) -&gt; fu=
n(X) -&gt; X end.</div><div>=C2=A0=C2=A0=C2=A0 g(Y) -&gt; case h() of Y -&g=
t; %code% end.<br></div><div><br></div><div>1. Could create an error due to=
 X being shadowed.</div><div>2. Could compile with inner X shadowed but a g=
uard is added by the Erlang compiler, comparing inner to the outer:</div><d=
iv>=C2=A0=C2=A0=C2=A0 f(X) -&gt; fun(X2) when X2 =3D:=3D X -&gt; X2 end.</d=
iv><div>=C2=A0=C2=A0=C2=A0 g(Y) -&gt; case h() of Y2 when Y2 =3D:=3D Y -&gt=
; %code% end.</div><div></div>3. Current behaviour: Inner X is shadowed and=
 is an entirely new variable (leads to naming and scoping confusion)<div><b=
r></div><div>Solution 2 does not introduce new syntax and matches expectati=
ons for many developers. The compiler fixes the confusion during the compil=
e time. Also should not break the existing code.<br></div></div><br><div cl=
ass=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Wed, 27 Apr 20=
22 at 21:11, Attila Rajmund Nohl &lt;<a href=3D"mailto:attila.r.nohl@gmail.=
com" target=3D"_blank">[email protected]</a>&gt; wrote:<br></div><blo=
ckquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left=
:1px solid rgb(204,204,204);padding-left:1ex">Fred Dushin &lt;<a href=3D"ma=
ilto:[email protected]" target=3D"_blank">[email protected]</a>&gt; ezt =C3=ADr=
ta (id=C5=91pont: 2022. =C3=A1pr. 27., Sze, 15:14):<br>
&gt;<br>
[...]<br>
&gt; But as noted later in the EEP, this can be written as<br>
&gt;<br>
&gt; f(X, Y) -&gt;<br>
&gt;=C2=A0 =C2=A0 =C2=A0F =3D fun ({a, Z}) when Z =3D:=3D Y=C2=A0 -&gt; {ok=
, Y};<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(_) -&gt; error<br>
&gt;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0end,<br>
&gt;=C2=A0 =C2=A0 =C2=A0F(X).<br>
[...]<br>
&gt; And not to be too critical, but I am having a hard time understanding =
parts of the Rationale section.=C2=A0 The author(s) suggest(s) that in curr=
ent Erlang temporary variables are needed to achieve the same as the propos=
ed glyph, but then provide an example that uses a guard (as above) but does=
n&#39;t use a temporary variable, after all (?)<br>
<br>
Z is the temporary variable in the above example.<br>
<br>
&gt; I am not sure what the problem is with temporary variables.=C2=A0 The =
compiler has registers at its disposal, so I don&#39;t think it&#39;s a per=
formance argument, but more an issue of readability, which again, I think i=
s a purely aesthetic question, and has no bearing on the features of the la=
nguage, per se.=C2=A0 In other words, the proposal is not suggesting anythi=
ng that cannot already be achieved in current Erlang, with effectively the =
same compiled BEAM ASM.<br>
<br>
Naming is hard, and if you have to come up with two meaningful<br>
variable names instead of one, it&#39;s even harder. This leads to code<br>
like this:<br>
<br>
=C2=A0 =C2=A0 =C2=A0F =3D fun ({a, TmpMeaningfulName}) when TmpMeaningfulNa=
me =3D:=3D<br>
MeaningfulName=C2=A0 -&gt; {ok, MeaningfulName};<br>
<br>
The current solution in the language easily leads to too long lines or<br>
overly verbose code.<br>
</blockquote></div>
</blockquote></div>

--00000000000070ad3005ddb5ad53--