New EEP draft: Pinning operator ^ in patterns

classic Classic list List threaded Threaded
256 messages Options
1234 ... 13
Reply | Threaded
Open this post in threaded view
|

New EEP draft: Pinning operator ^ in patterns

Richard Carlsson-3
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp

eep-00xx.md (13K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Fred Hebert-2
On 12/24, Richard Carlsson wrote:

>In this case, there is no new binding of `Y`, and the use of `Y` in
>the fun clause body refers to the function parameter.  But it is also
>possible to combine pinning and shadowing in the same pattern:
>
>    f(X, Y) ->
>        F = fun ({a, ^Y, Y})  -> {ok, Y};
>                (_) -> error
>            end,
>        F(X).
>
>In this case, the pinned field refers to the value of the function
>function parameter, but there is also a new shadowing binding of `Y`
>to the third field of the tuple.  The use in the fun clause body now
>refers to the shadowing instance.
>
>Generator patterns in list comprehensions or binary comprehensions
>follow the same rules as fun clause heads, so with pinning we can for
>example write the following code:
>
>    f(X, Y) ->
>        [{b, Y} || {a, ^Y, Y} <- X].
>
>where the `Y` in `{b, Y}` is the shadowing instance bound to the third
>element of the pattern tuple.
>

I don't understand the rationale here. If ^Y and Y are distinct in these
patterns, they should be named differently. It sounds unsafe to let the
values diverge.  The rationale in fact tries to expand on it there:

>Furthermore, in the odd case that the pattern would both need to
>access the surrounding definition of `Y` as well as introduce a new
>shadowing binding, this can be easily written using pinning:
>
>    f(X, Y) ->
>        F = fun ({a, ^Y, Y})  -> {ok, Y};
>                (_) -> error
>            end,
>        F(X).
>
>but in current Erlang, two separate temporary variables would be
>required:
>
>    f(X, Y) ->
>        OuterY = Y,
>        F = fun ({a, Temp, Y}) when Temp =:= OuterY -> {ok, Y};
>                (_) -> error
>            end,
>        F(X).
>

But it still makes no sense why you would require Temp to be equal to Y
but only use the inner binding specifically. Semantically there is no
benefit to be had there, and this introduces a rebinding that isn't
required?

The one implementation detail that could have an impact there would be
based on the values of binaries references where there is specifically a
desire to use one more than the other.

I'm opposed only to this specific part of the RFC as not helping making
things clearer. Just name the variables differently and keep the
shadowing warning.
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Tristan Sloughter-4
In reply to this post by Richard Carlsson-3
I'd much prefer if `^` was used to allow rebinding. But I know that would be too confusing since Elixir went with ^ to mean regular binding. So maybe `!`?

But this change just lets you add the `^` while not changing how anything works? That sounds like it'll cause a lot of confusion when reading since sometimes a bind is using `^` and sometimes they aren't but it is doing the same thing...

On Thu, Dec 24, 2020, at 13:10, Richard Carlsson wrote:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also 

Ho ho ho,

        /Richard & the good folks at WhatsApp

Attachments:
  • eep-00xx.md

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Michael Truog
In reply to this post by Richard Carlsson-3
In Elixir syntax, with variables getting rebound in all source code it makes sense that Elixir needs the pin operator ( ^ ) to say "Hey, this is different, this isn't the imperative programming variable assignment [match] you are accustom to!".

Erlang is different, with clearly immutable variable data.  A consistent variable name in the source code without a syntactic sugar ^ prefix helps to prevent extra work changing unrelated source code in the future as source code changes.  The variable name is always referred to as the same name in the same way, to refer to the same data.  That sameness helps developers understand the source code and adding a prefix would obscure the variable name while attempting to communicate that matching on a variable is somehow different from normal, though it is very normal in Erlang.

Matching is very important in Prolog too, with Erlang syntax being naturally similar.  A pin operator hasn't been necessary in Prolog syntax, though the matching gets more complex in Prolog (solving rubik's cubes with short examples and other amazing things).  There isn't a clear benefit to adding the Elixir pin operator to Erlang, just like there wouldn't be a benefit to adding it to Prolog.

Best Regards,
Michael

On 12/24/20 12:10 PM, Richard Carlsson wrote:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

zxq9-2
In reply to this post by Tristan Sloughter-4
Looking at this from the perspective of having to read other people's
code quite a lot, I am pretty unexcited about any changes that would
introduce glyphy anotations to variable names or move even slightly in
the direction of allowing rebinding.

Few long-time Erlangers would ever feel a desire to use a feature like
this, but people who have no idea how the paradigm works are very likely
to overuse it to a fault, making code a nightmare to sort through. This
particular change with the ^ isn't so bad (but why not just use a new
name? Y becomes Y1 or NewY or NextY instead of ^Y? I usually prefer
something that is at least *descriptive* anyway like PopulatedY or
FilteredY or whatever. The keystrokes are free...) but again, moving in
the direction of rebinding is really pushing things in a crazy direction.

I find Erlang's approach to strict single assignment one of its greatest
benefits because I can sort through convoluted code written by newcomers
*much* faster than I can in most other languages specifically because I
can know for certain where a value came from at a glance.

-Craig

On 2020/12/25 7:36, Tristan Sloughter wrote:
> I'd much prefer if `^` was used to allow rebinding. But I know that
> would be too confusing since Elixir went with ^ to mean regular binding.
> So maybe `!`?
>
> But this change just lets you add the `^` while not changing how
> anything works? That sounds like it'll cause a lot of confusion when
> reading since sometimes a bind is using `^` and sometimes they aren't
> but it is doing the same thing...
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Eric Pailleau

💯


Envoyé depuis mon mobile



---- zxq9 a écrit ----

Looking at this from the perspective of having to read other people's

code quite a lot, I am pretty unexcited about any changes that would

introduce glyphy anotations to variable names or move even slightly in

the direction of allowing rebinding.



Few long-time Erlangers would ever feel a desire to use a feature like

this, but people who have no idea how the paradigm works are very likely

to overuse it to a fault, making code a nightmare to sort through. This

particular change with the ^ isn't so bad (but why not just use a new

name? Y becomes Y1 or NewY or NextY instead of ^Y? I usually prefer

something that is at least *descriptive* anyway like PopulatedY or

FilteredY or whatever. The keystrokes are free...) but again, moving in

the direction of rebinding is really pushing things in a crazy direction.



I find Erlang's approach to strict single assignment one of its greatest

benefits because I can sort through convoluted code written by newcomers

*much* faster than I can in most other languages specifically because I

can know for certain where a value came from at a glance.



-Craig



On 2020/12/25 7:36, Tristan Sloughter wrote:

> I'd much prefer if `^` was used to allow rebinding. But I know that

> would be too confusing since Elixir went with ^ to mean regular binding.

> So maybe `!`?

>

> But this change just lets you add the `^` while not changing how

> anything works? That sounds like it'll cause a lot of confusion when

> reading since sometimes a bind is using `^` and sometimes they aren't

> but it is doing the same thing...

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Mikael Karlsson-7
In reply to this post by Richard Carlsson-3
You are not pulling legs with a Christmas joke now Richard?
It looks too ambitious to be that, but I cannot get rid of the feeling.

elixirian: :this_parrot = :alive
erlanger: :this_parrot = :now_destructed
elixirian: :this_parrot = :no_no_only_de_con_structed

ho ho ho,
Mikael


Den tors 24 dec. 2020 kl 21:10 skrev Richard Carlsson <[hidden email]>:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Mikael Karlsson-7
OK, I never get those ":" right, this_parrot was not meant to be an atom but a variable.
this_parrot = ... etc.

Den fre 25 dec. 2020 kl 11:41 skrev Mikael Karlsson <[hidden email]>:
You are not pulling legs with a Christmas joke now Richard?
It looks too ambitious to be that, but I cannot get rid of the feeling.

elixirian: :this_parrot = :alive
erlanger: :this_parrot = :now_destructed
elixirian: :this_parrot = :no_no_only_de_con_structed

ho ho ho,
Mikael


Den tors 24 dec. 2020 kl 21:10 skrev Richard Carlsson <[hidden email]>:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Igor Clark
In reply to this post by Eric Pailleau
Hello,

Just to give a flavour from a relative newcomer compared to others responding here, I think the simplicity of Erlang-the-language is a huge benefit, and I’d love to avoid what I can’t help seeing as ambiguities, even when intended to clarify.

I’ve only been writing Erlang in anger for maybe 4 years spread over the last 10, and haven’t written large libraries myself, but I’ve never had an instance in my code where the kind of rebinding issue mentioned has been more than a temporary clash, usually easily solved by attending to compiler warnings, and I’ve never had an actual bug as a result of it.

On top of that I personally would really like on principle to steer clear of new signs and symbols annotating variables. The lack (or restriction to its absolute minimum) of all that is one of the big benefits of Erlang, especially in combination with the immutability - like Craig says it just means you know what you’ve got and can trace it/“reason about it” more confidently, and in my experience at least the question of clashes/rebinding have been edge cases, easily trapped.

I don’t know enough about Elixir to have an opinion on its use there, but to me personally, it just doesn’t seem necessary in Erlang, and for my purposes would serve mostly to add visual noise and complexity that don’t seem at home there.

Of course, it’s just my holiday-time 2 cents, and I realise I’d be free not to use it if I chose not to - as someone who mostly writes erlang alone this would often be fine, but it would gradually start to crop up in libraries and deps.

Happy holidays to all,
Igor


On 25 Dec 2020, at 09:46, Eric Pailleau <[hidden email]> wrote:



💯


Envoyé depuis mon mobile



---- zxq9 a écrit ----

Looking at this from the perspective of having to read other people's

code quite a lot, I am pretty unexcited about any changes that would

introduce glyphy anotations to variable names or move even slightly in

the direction of allowing rebinding.



Few long-time Erlangers would ever feel a desire to use a feature like

this, but people who have no idea how the paradigm works are very likely

to overuse it to a fault, making code a nightmare to sort through. This

particular change with the ^ isn't so bad (but why not just use a new

name? Y becomes Y1 or NewY or NextY instead of ^Y? I usually prefer

something that is at least *descriptive* anyway like PopulatedY or

FilteredY or whatever. The keystrokes are free...) but again, moving in

the direction of rebinding is really pushing things in a crazy direction.



I find Erlang's approach to strict single assignment one of its greatest

benefits because I can sort through convoluted code written by newcomers

*much* faster than I can in most other languages specifically because I

can know for certain where a value came from at a glance.



-Craig



On 2020/12/25 7:36, Tristan Sloughter wrote:

> I'd much prefer if `^` was used to allow rebinding. But I know that

> would be too confusing since Elixir went with ^ to mean regular binding.

> So maybe `!`?

>

> But this change just lets you add the `^` while not changing how

> anything works? That sounds like it'll cause a lot of confusion when

> reading since sometimes a bind is using `^` and sometimes they aren't

> but it is doing the same thing...

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Loïc Hoguin-3
In reply to this post by Richard Carlsson-3
I get the feeling, occasionally you move code around and variables end
up having the same name and things break. But it's so rare that 1- you
forgot to check variable names and this happens; and 2- tests don't
catch the mistake; and 3- code review doesn't notice the problem, that I
don't think I would end up using this.

Merry Christmas!

On 24/12/2020 21:10, Richard Carlsson wrote:

> The ^ operator allows you to annotate already-bound pattern variables as
> ^X, like in Elixir. This is less error prone when code is being
> refactored and moved around so that variables previously new in a
> pattern may become bound, or vice versa, and makes it easier for the
> reader to see the intent of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>          /Richard & the good folks at WhatsApp

--
Loïc Hoguin
https://ninenines.eu
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Richard O'Keefe
In reply to this post by Richard Carlsson-3
"This fills a much-needed gap."

Erlang functions are as a rule small enough that you
shouldn't ever shadow a variable.  One of the worst
features of Erlang is that you can write
   foo(X) -> fun (X) -> fun (X) -> 1 end end.
and have three different variables all called X, and
one of the good things about erlc is that it tells you.
foo.erl:3: Warning: variable 'X' is unused
foo.erl:3: Warning: variable 'X' is unused
foo.erl:3: Warning: variable 'X' shadowed in 'fun'
foo.erl:3: Warning: variable 'X' shadowed in 'fun'

The very last thing we want is a notation that lets
us have two different variables with the same name
in a single pattern.


On Fri, 25 Dec 2020 at 09:10, Richard Carlsson <[hidden email]> wrote:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Anthony Ramine-4
That's already a thing in Erlang and AFAIK that never caused issues for anyone.

X = 2.
F = fun (<<_:X/binary>>, X) -> oh end.
F(<<0, 0>>, 3).

> Le 25 déc. 2020 à 23:41, Richard O'Keefe <[hidden email]> a écrit :
>
> "This fills a much-needed gap."
>
> Erlang functions are as a rule small enough that you
> shouldn't ever shadow a variable.  One of the worst
> features of Erlang is that you can write
>    foo(X) -> fun (X) -> fun (X) -> 1 end end.
> and have three different variables all called X, and
> one of the good things about erlc is that it tells you.
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
>
> The very last thing we want is a notation that lets
> us have two different variables with the same name
> in a single pattern.
>
>
> On Fri, 25 Dec 2020 at 09:10, Richard Carlsson <[hidden email]> wrote:
> The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Richard O'Keefe
(a) Yes that kind of thing DOES cause headaches for me.
    There is no excuse for writing code like that and
    never has been.
(b) After correcting two of the full stops and wrapping
    these lines in a function and a -module,
f.erl:6: Warning: variable 'X' is unused
f.erl:6: Warning: variable 'X' shadowed in 'fun'

The very last thing we need is a notation that makes it
easier to write pointlessly bad code.

If the proposed notation solved some other practical
problem, we could live with the downsides.  But while
the Erlang shoes have given me many a blister over the
years, this EEP would have done nothing to help.

One thing I *would* like is an analogue of -Werror.


On Sun, 27 Dec 2020 at 02:16, Anthony Ramine <[hidden email]> wrote:
That's already a thing in Erlang and AFAIK that never caused issues for anyone.

X = 2.
F = fun (<<_:X/binary>>, X) -> oh end.
F(<<0, 0>>, 3).

> Le 25 déc. 2020 à 23:41, Richard O'Keefe <[hidden email]> a écrit :
>
> "This fills a much-needed gap."
>
> Erlang functions are as a rule small enough that you
> shouldn't ever shadow a variable.  One of the worst
> features of Erlang is that you can write
>    foo(X) -> fun (X) -> fun (X) -> 1 end end.
> and have three different variables all called X, and
> one of the good things about erlc is that it tells you.
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
>
> The very last thing we want is a notation that lets
> us have two different variables with the same name
> in a single pattern.
>
>
> On Fri, 25 Dec 2020 at 09:10, Richard Carlsson <[hidden email]> wrote:
> The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp

Reply | Threaded
Open this post in threaded view
|

RE: New EEP draft: Pinning operator ^ in patterns

wojteksurowka
> The very last thing we need is a notation that makes it
> easier to write pointlessly bad code.

I can only agree.
 
> One thing I *would* like is an analogue of -Werror.

Maybe I misunderstood this, but erlc has -WError option which
makes warnings into errors, exactly like -Werror in C/C++
compilers. It can be also specified in rebar3 config file with
warnings_as_errors.

Regards,
Wojtek

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Michael Truog
In reply to this post by Richard O'Keefe
On 12/27/20 3:39 PM, Richard O'Keefe wrote:

One thing I *would* like is an analogue of -Werror.
We have the option warnings_as_errors, so we have that now (added in R13B04).

Best Regards,
Michael




On Sun, 27 Dec 2020 at 02:16, Anthony Ramine <[hidden email]> wrote:
That's already a thing in Erlang and AFAIK that never caused issues for anyone.

X = 2.
F = fun (<<_:X/binary>>, X) -> oh end.
F(<<0, 0>>, 3).

> Le 25 déc. 2020 à 23:41, Richard O'Keefe <[hidden email]> a écrit :
>
> "This fills a much-needed gap."
>
> Erlang functions are as a rule small enough that you
> shouldn't ever shadow a variable.  One of the worst
> features of Erlang is that you can write
>    foo(X) -> fun (X) -> fun (X) -> 1 end end.
> and have three different variables all called X, and
> one of the good things about erlc is that it tells you.
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' is unused
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
> foo.erl:3: Warning: variable 'X' shadowed in 'fun'
>
> The very last thing we want is a notation that lets
> us have two different variables with the same name
> in a single pattern.
>
>
> On Fri, 25 Dec 2020 at 09:10, Richard Carlsson <[hidden email]> wrote:
> The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp


_______________________________________________
eeps mailing list
[hidden email]
http://erlang.org/mailman/listinfo/eeps

Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Attila Rajmund Nohl
In reply to this post by Richard Carlsson-3
Richard Carlsson <[hidden email]> ezt írta (időpont: 2020.
dec. 24., Cs, 21:10):

>
> The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> But
> suppose that someone adds a binding of the name `T` further up in the
> function body, without noticing that the name is already in use:
>
>   g(Stuff) ->
>       ...
>      T = q(Stuff) + 1,
>       io:format("~p", [p(T)]),
>       ...
>       Thing = case ... of
>                   {a, T} -> T;
>                   _ -> 0
>               end,
>       ...
>       {ok, [Thing|Stuff]}.
>
> Now the first clause of the case switch will only match if the second
> element of the tuple has the exact same value as the previously
> defined `T`.

I admit I had a similar bug a couple of months ago. Took some time to
find it, so I see the rationale to introduce an option to tell the
compiler "I expect the variable to be pinned here". But I don't think
this is the right approach to avoid these kinds of bugs. The ^
operator must be optional at introduction (to avoid breaking lots of
legacy code), but if it's optional, it's usage will be also optional -
and more importantly inconsistent. Some codebases will use, some not
and in the worst case it's usage could be on a function-by-function
clause, meaning that if I don't see the ^ operator in code like above,
I can't be sure it was intentionally or accidentally left out. I'm not
sure that a code like

case ... of
  {a, ^T} -> T;

will be more clear than

case ... of
  {a, TempT} when T == TempT -> TempT;

For this later case we don't need a new operator and the code would be
more consistent with the idioms used in funs to avoid shadowing.

> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Rickard Green-2
In reply to this post by Richard Carlsson-3
On Thu, Dec 24, 2020 at 9:10 PM Richard Carlsson <[hidden email]> wrote:
The ^ operator allows you to annotate already-bound pattern variables as ^X, like in Elixir. This is less error prone when code is being refactored and moved around so that variables previously new in a pattern may become bound, or vice versa, and makes it easier for the reader to see the intent of the code.

See also https://github.com/erlang/otp/pull/2951

Ho ho ho,

        /Richard & the good folks at WhatsApp

I've added your attached EEP draft as EEP 55: https://github.com/erlang/eep/blob/master/eeps/eep-0055.md

Regards,
Rickard
--
Rickard Green, Erlang/OTP, Ericsson AB
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Raimo Niskanen-11
In reply to this post by Richard Carlsson-3
As others have said: for Elixir this operator is essential, since they
rebind variables without it.

For Erlang, if using a pinning operator had been required from the start;
I think that would have been a bit better than the current "match
if already bound".  It is hard to be sure by looking at the code
if the variable is already bound - you have to make a machine search.

Introducing a pinning operator now is trickier...

Having a compiler option to choose if pinning is allowed/required makes it
hard to know what to expect from the code.  The compiler option is set in
some Makefile far away from the source code.

I think I would prefer that instead there should be a compiler pragma
(I wish it would not be allowed from an include file but that is probably
impossible to enforce) so it is visible in the current module what to
expect about operator pinning.  Without the pragma the pinning operator is
not allowed, with it pinning is mandatory; not a warning - an error if
a pinning operator is missing.

You get the idea: it should be possible from the source code how to read
it, at least on the module level.

How to take the next step i.e when code not using pinning is the exception,
to remove the compiler pragma, I have not thought about yet...

Cheers
/ Raimo Niskanen



On Thu, Dec 24, 2020 at 09:10:17PM +0100, Richard Carlsson wrote:

> The ^ operator allows you to annotate already-bound pattern variables as
> ^X, like in Elixir. This is less error prone when code is being refactored
> and moved around so that variables previously new in a pattern may become
> bound, or vice versa, and makes it easier for the reader to see the intent
> of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp

>     Author: Richard carlsson <carlsson.richard(at)gmail(dot)com>
>     Status: Draft
>     Type: Standards Track
>     Created: 21-Dec-2020
>     Erlang-Version: 24
>     Post-History: 24-Dec-2020
> ****
> EEP XXX: Pinning operator ^ in patterns
> ----
>
>
> Abstract
> ========
>
> This EEP proposes the addition of a new unary operator `^` for
> explicitly marking variables in patterns as being already bound.  This
> is known as "pinning" in Elixir - see [Elixir doc][the Elixir
> documentation].
>
> For example:
>
>     f(X, Y) ->
>         case X of
>             {a, Y} -> ok;
>             _ -> error
>         end.
>
> could be written more explicitly:
>
>     f(X, Y) ->
>         case X of
>             {a, ^Y} -> ok;
>             _ -> error
>         end.
>
> In Elixir, this operator is strictly necessary for being able to refer
> to the value of a bound variable as part of a pattern, because
> variables in patterns are always regarded as being new shadowing
> instances (like in Erlang's fun clause heads), unless explicitly
> pinned.
>
> In Erlang, they would be optional, but are still a good idea because
> they make programs more robust under edits and refactorings, and
> furthermore allow the use of pinned variables in fun clause heads and
> in comprehension generator patterns.
>
>
> Specification
> =============
>
> A new unary operator `^` is added to Erlang, called the "pinning
> operator".  It may only be used in patterns, and only on variables.
> Its meaning is that the "pinned" variable is to be interpreted in the
> enclosing environment of the pattern, and its value used in its place
> for that position in the pattern.
>
> In current Erlang, this behaviour is what happens automatically in
> ordinary matching constructs if the variable is already bound in the
> enclosing environment.  In the following example:
>
>     f(X, Y) ->
>         case X of
>             {a, Y} -> {ok, Y};
>             _ -> error
>         end.
>
> the use of `Y` in the pattern is regarded as a reference to the
> function parameter `Y`, instead of as introducing a new variable, and
> the `Y` in the clause body is then that same parameter.  Therefore,
> annotating the pattern variable as `^Y` in this case does not change
> the behaviour of the program, but makes the intent explicit:
>
>     f(X, Y) ->
>         case X of
>             {a, ^Y} -> {ok, Y};
>             _ -> error
>         end.
>
> For fun expressions and list comprehension generator patterns, the
> pinning operator makes the language more expressive.  Take the
> following Erlang code:
>
>     f(X, Y) ->
>         F = fun ({a, Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Here, the occurrence of `Y` in the clause head of the fun `F` is a new
> variable instance, shadowing the `Y` parameter of `f(X, Y)`, and the
> fun clause will match any value in that position.  The `Y` in the
> clause body is the one bound in the clause head.  However, using the
> pinning operator, we can selectively match on variables bound in the
> outer scope:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> In this case, there is no new binding of `Y`, and the use of `Y` in
> the fun clause body refers to the function parameter.  But it is also
> possible to combine pinning and shadowing in the same pattern:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y, Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> In this case, the pinned field refers to the value of the function
> function parameter, but there is also a new shadowing binding of `Y`
> to the third field of the tuple.  The use in the fun clause body now
> refers to the shadowing instance.
>
> Generator patterns in list comprehensions or binary comprehensions
> follow the same rules as fun clause heads, so with pinning we can for
> example write the following code:
>
>     f(X, Y) ->
>         [{b, Y} || {a, ^Y, Y} <- X].
>
> where the `Y` in `{b, Y}` is the shadowing instance bound to the third
> element of the pattern tuple.
>
> Finally, a new compiler flag `warn_unpinned_vars` is added, disabled
> by default, which if enabled makes the compiler emit warnings about
> all uses of already bound variables in patterns that are not
> explicitly annotated with the `^` operator.  This allows users to
> migrate their code module by module towards using explicit pinning in
> all their code.  If pinning becomes the norm in Erlang, this flag
> could be turned on by default, and eventually, the pinning operator
> could become strictly required for referring to already bound
> variables in patterns.
>
>
> Rationale
> =========
>
> The explicit pinning of variables in patterns make programs more
> readable, because the intent of the code becomes clear.  When already
> bound variables are used in Erlang without any annotation, anyone
> reading a piece of code must first study it closely to understand
> which variables will be bound at the point of a pattern, before they
> can tell whether any pattern variable is a new binding or implies an
> equality assertion.  This is easy to miss even for experienced
> Erlangers, be it during code reviews or while trying to understand a
> piece of poorly commented code.
>
> Perhaps more importantly, pinning also makes programs more robust
> under edits and refactorings.  Take our previous example, and add a
> print statement:
>
>     f(X, Y) ->
>         io:format("checking: ~p", [Y]),
>         case X of
>             {a, Y} -> {ok, Y};
>             _ -> error
>         end.
>
> Suppose someone renames the function parameter from `Y` to `Z` and
> updates the print statement but forgets to update the use in the case
> clause.  Without an explicit pinning annotation, the change would be
> quietly allowed, but the `Y` in the pattern would be interpreted as a
> new variable that will match any value, which will then be used in the
> body.  This changes the behaviour of the program.  If the use in the
> pattern had been annotated as `^Y`, the compiler would have generated
> an error "Y is unbound" and the mistake would have been caught.
>
> When code is being modified to add a feature or fix a bug, a
> programmer might want to introduce a new variable for a temporary
> result.  In a long function body, this risks introducing a new bug.
> Consider the following:
>
>     g(Stuff) ->
>        ...
>        Thing = case ... of
>                    {a, T} -> T;
>                    _ -> 0
>                end,
>        ...
>        {ok, [Thing|Stuff]}.
>
> Here, `T` is a new variable, clearly intended as just a temporary and
> local variable for extracting the second element of the tuple.  But
> suppose that someone adds a binding of the name `T` further up in the
> function body, without noticing that the name is already in use:
>
>     g(Stuff) ->
>        ...
>        T = q(Stuff) + 1,
>        io:format("~p", [p(T)]),
>        ...
>        Thing = case ... of
>                    {a, T} -> T;
>                    _ -> 0
>                end,
>        ...
>        {ok, [Thing|Stuff]}.
>
> Now the first clause of the case switch will only match if the second
> element of the tuple has the exact same value as the previously
> defined `T`.  Again, the compiler quietly accepts this change, while
> if it had been instructed to warn about all non-annotated uses of
> already bound variables in patterns, this mistake would have been
> detected.
>
>
> Shadowing in Funs and Comprehensions
> ------------------------------------
>
> In funs and comprehensions, pinning also lets us do things that
> otherwise requires additional temporary variables.  Consider the
> following code:
>
>     f(X, Y) ->
>         F = fun ({a, Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Since the `Y` in the clause head of the fun is a new shadowing
> instance, the pattern will match any value in that position.  To match
> only the value passed as `Y` to `f`, a clause guard must be added, and
> a temporary variable be used to access the outer `Y`:
>
>     f(X, Y) ->
>         OuterY = Y,
>         F = fun ({a, Y}) when Y =:= OuterY -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> We could instead rename the inner use of `Y` to avoid shadowing, but
> the equality test must still be written as an explicit guard:
>
>     f(X, Y) ->
>         F = fun ({a, Z}) when Z =:= Y -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> With the help of the pinning operator, such things are no longer a
> concern, and we can simply write:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Furthermore, in the odd case that the pattern would both need to
> access the surrounding definition of `Y` as well as introduce a new
> shadowing binding, this can be easily written using pinning:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y, Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> but in current Erlang, two separate temporary variables would be
> required:
>
>     f(X, Y) ->
>         OuterY = Y,
>         F = fun ({a, Temp, Y}) when Temp =:= OuterY -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> As explained before, the same goes for patterns in generators of
> comprehensions.
>
>
>
> Backwards Compatibility
> =======================
>
> The addition of a new and previously unused operator `^` does not
> affect the meaning of existing code, and the compiler will not emit
> any new warnings or errors for existing code, unless explicitly
> enabled with `warn_unpinned_vars`.  This change is therefore fully
> backwards compatible.
>
>
>
> Implementation
> ==============
>
> The implementation can be found in [PR #2951][pr].
>
>
>
> Copyright
> =========
>
> This document has been placed in the public domain.
>
>
> [Elixir doc]: https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator
>     "Elixir pattern matching - the pin operator"
>
> [pr]: https://github.com/erlang/otp/pull/2951
>     "#2951: Add a new operator ^ for pinning of pattern variables"
>
>
>
> [EmacsVar]: <> "Local Variables:"
> [EmacsVar]: <> "mode: indented-text"
> [EmacsVar]: <> "indent-tabs-mode: nil"
> [EmacsVar]: <> "sentence-end-double-space: t"
> [EmacsVar]: <> "fill-column: 70"
> [EmacsVar]: <> "coding: utf-8"
> [EmacsVar]: <> "End:"
> [VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: "


--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Richard Carlsson-3
The way I planned it is:
  1. Even from the start, pinning will always be allowed, without requiring any flag to opt in. This does not tell you about existing uses of already-bound variables, but you can start using pinning right away for readability and for avoiding bugs when refactoring. The compiler will always tell you if a pinned variable doesn't exist, so you don't accidentally accept any value in that position.
  2. You can enable warnings at your own pace in order to start cleaning up your code.
  3. In a following major release, the warnings will be on by default, but you can disable them to compile old code.
  4. In a distant future, it might become an error to not use ^ to mark already-bound variables.

        /Richard


Den tors 14 jan. 2021 kl 13:33 skrev Raimo Niskanen <[hidden email]>:
As others have said: for Elixir this operator is essential, since they
rebind variables without it.

For Erlang, if using a pinning operator had been required from the start;
I think that would have been a bit better than the current "match
if already bound".  It is hard to be sure by looking at the code
if the variable is already bound - you have to make a machine search.

Introducing a pinning operator now is trickier...

Having a compiler option to choose if pinning is allowed/required makes it
hard to know what to expect from the code.  The compiler option is set in
some Makefile far away from the source code.

I think I would prefer that instead there should be a compiler pragma
(I wish it would not be allowed from an include file but that is probably
impossible to enforce) so it is visible in the current module what to
expect about operator pinning.  Without the pragma the pinning operator is
not allowed, with it pinning is mandatory; not a warning - an error if
a pinning operator is missing.

You get the idea: it should be possible from the source code how to read
it, at least on the module level.

How to take the next step i.e when code not using pinning is the exception,
to remove the compiler pragma, I have not thought about yet...

Cheers
/ Raimo Niskanen



On Thu, Dec 24, 2020 at 09:10:17PM +0100, Richard Carlsson wrote:
> The ^ operator allows you to annotate already-bound pattern variables as
> ^X, like in Elixir. This is less error prone when code is being refactored
> and moved around so that variables previously new in a pattern may become
> bound, or vice versa, and makes it easier for the reader to see the intent
> of the code.
>
> See also https://github.com/erlang/otp/pull/2951
>
> Ho ho ho,
>
>         /Richard & the good folks at WhatsApp

>     Author: Richard carlsson <carlsson.richard(at)gmail(dot)com>
>     Status: Draft
>     Type: Standards Track
>     Created: 21-Dec-2020
>     Erlang-Version: 24
>     Post-History: 24-Dec-2020
> ****
> EEP XXX: Pinning operator ^ in patterns
> ----
>
>
> Abstract
> ========
>
> This EEP proposes the addition of a new unary operator `^` for
> explicitly marking variables in patterns as being already bound.  This
> is known as "pinning" in Elixir - see [Elixir doc][the Elixir
> documentation].
>
> For example:
>
>     f(X, Y) ->
>         case X of
>             {a, Y} -> ok;
>             _ -> error
>         end.
>
> could be written more explicitly:
>
>     f(X, Y) ->
>         case X of
>             {a, ^Y} -> ok;
>             _ -> error
>         end.
>
> In Elixir, this operator is strictly necessary for being able to refer
> to the value of a bound variable as part of a pattern, because
> variables in patterns are always regarded as being new shadowing
> instances (like in Erlang's fun clause heads), unless explicitly
> pinned.
>
> In Erlang, they would be optional, but are still a good idea because
> they make programs more robust under edits and refactorings, and
> furthermore allow the use of pinned variables in fun clause heads and
> in comprehension generator patterns.
>
>
> Specification
> =============
>
> A new unary operator `^` is added to Erlang, called the "pinning
> operator".  It may only be used in patterns, and only on variables.
> Its meaning is that the "pinned" variable is to be interpreted in the
> enclosing environment of the pattern, and its value used in its place
> for that position in the pattern.
>
> In current Erlang, this behaviour is what happens automatically in
> ordinary matching constructs if the variable is already bound in the
> enclosing environment.  In the following example:
>
>     f(X, Y) ->
>         case X of
>             {a, Y} -> {ok, Y};
>             _ -> error
>         end.
>
> the use of `Y` in the pattern is regarded as a reference to the
> function parameter `Y`, instead of as introducing a new variable, and
> the `Y` in the clause body is then that same parameter.  Therefore,
> annotating the pattern variable as `^Y` in this case does not change
> the behaviour of the program, but makes the intent explicit:
>
>     f(X, Y) ->
>         case X of
>             {a, ^Y} -> {ok, Y};
>             _ -> error
>         end.
>
> For fun expressions and list comprehension generator patterns, the
> pinning operator makes the language more expressive.  Take the
> following Erlang code:
>
>     f(X, Y) ->
>         F = fun ({a, Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Here, the occurrence of `Y` in the clause head of the fun `F` is a new
> variable instance, shadowing the `Y` parameter of `f(X, Y)`, and the
> fun clause will match any value in that position.  The `Y` in the
> clause body is the one bound in the clause head.  However, using the
> pinning operator, we can selectively match on variables bound in the
> outer scope:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> In this case, there is no new binding of `Y`, and the use of `Y` in
> the fun clause body refers to the function parameter.  But it is also
> possible to combine pinning and shadowing in the same pattern:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y, Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> In this case, the pinned field refers to the value of the function
> function parameter, but there is also a new shadowing binding of `Y`
> to the third field of the tuple.  The use in the fun clause body now
> refers to the shadowing instance.
>
> Generator patterns in list comprehensions or binary comprehensions
> follow the same rules as fun clause heads, so with pinning we can for
> example write the following code:
>
>     f(X, Y) ->
>         [{b, Y} || {a, ^Y, Y} <- X].
>
> where the `Y` in `{b, Y}` is the shadowing instance bound to the third
> element of the pattern tuple.
>
> Finally, a new compiler flag `warn_unpinned_vars` is added, disabled
> by default, which if enabled makes the compiler emit warnings about
> all uses of already bound variables in patterns that are not
> explicitly annotated with the `^` operator.  This allows users to
> migrate their code module by module towards using explicit pinning in
> all their code.  If pinning becomes the norm in Erlang, this flag
> could be turned on by default, and eventually, the pinning operator
> could become strictly required for referring to already bound
> variables in patterns.
>
>
> Rationale
> =========
>
> The explicit pinning of variables in patterns make programs more
> readable, because the intent of the code becomes clear.  When already
> bound variables are used in Erlang without any annotation, anyone
> reading a piece of code must first study it closely to understand
> which variables will be bound at the point of a pattern, before they
> can tell whether any pattern variable is a new binding or implies an
> equality assertion.  This is easy to miss even for experienced
> Erlangers, be it during code reviews or while trying to understand a
> piece of poorly commented code.
>
> Perhaps more importantly, pinning also makes programs more robust
> under edits and refactorings.  Take our previous example, and add a
> print statement:
>
>     f(X, Y) ->
>         io:format("checking: ~p", [Y]),
>         case X of
>             {a, Y} -> {ok, Y};
>             _ -> error
>         end.
>
> Suppose someone renames the function parameter from `Y` to `Z` and
> updates the print statement but forgets to update the use in the case
> clause.  Without an explicit pinning annotation, the change would be
> quietly allowed, but the `Y` in the pattern would be interpreted as a
> new variable that will match any value, which will then be used in the
> body.  This changes the behaviour of the program.  If the use in the
> pattern had been annotated as `^Y`, the compiler would have generated
> an error "Y is unbound" and the mistake would have been caught.
>
> When code is being modified to add a feature or fix a bug, a
> programmer might want to introduce a new variable for a temporary
> result.  In a long function body, this risks introducing a new bug.
> Consider the following:
>
>     g(Stuff) ->
>        ...
>        Thing = case ... of
>                    {a, T} -> T;
>                    _ -> 0
>                end,
>        ...
>        {ok, [Thing|Stuff]}.
>
> Here, `T` is a new variable, clearly intended as just a temporary and
> local variable for extracting the second element of the tuple.  But
> suppose that someone adds a binding of the name `T` further up in the
> function body, without noticing that the name is already in use:
>
>     g(Stuff) ->
>        ...
>        T = q(Stuff) + 1,
>        io:format("~p", [p(T)]),
>        ...
>        Thing = case ... of
>                    {a, T} -> T;
>                    _ -> 0
>                end,
>        ...
>        {ok, [Thing|Stuff]}.
>
> Now the first clause of the case switch will only match if the second
> element of the tuple has the exact same value as the previously
> defined `T`.  Again, the compiler quietly accepts this change, while
> if it had been instructed to warn about all non-annotated uses of
> already bound variables in patterns, this mistake would have been
> detected.
>
>
> Shadowing in Funs and Comprehensions
> ------------------------------------
>
> In funs and comprehensions, pinning also lets us do things that
> otherwise requires additional temporary variables.  Consider the
> following code:
>
>     f(X, Y) ->
>         F = fun ({a, Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Since the `Y` in the clause head of the fun is a new shadowing
> instance, the pattern will match any value in that position.  To match
> only the value passed as `Y` to `f`, a clause guard must be added, and
> a temporary variable be used to access the outer `Y`:
>
>     f(X, Y) ->
>         OuterY = Y,
>         F = fun ({a, Y}) when Y =:= OuterY -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> We could instead rename the inner use of `Y` to avoid shadowing, but
> the equality test must still be written as an explicit guard:
>
>     f(X, Y) ->
>         F = fun ({a, Z}) when Z =:= Y -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> With the help of the pinning operator, such things are no longer a
> concern, and we can simply write:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y}) -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> Furthermore, in the odd case that the pattern would both need to
> access the surrounding definition of `Y` as well as introduce a new
> shadowing binding, this can be easily written using pinning:
>
>     f(X, Y) ->
>         F = fun ({a, ^Y, Y})  -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> but in current Erlang, two separate temporary variables would be
> required:
>
>     f(X, Y) ->
>         OuterY = Y,
>         F = fun ({a, Temp, Y}) when Temp =:= OuterY -> {ok, Y};
>                 (_) -> error
>             end,
>         F(X).
>
> As explained before, the same goes for patterns in generators of
> comprehensions.
>
>
>
> Backwards Compatibility
> =======================
>
> The addition of a new and previously unused operator `^` does not
> affect the meaning of existing code, and the compiler will not emit
> any new warnings or errors for existing code, unless explicitly
> enabled with `warn_unpinned_vars`.  This change is therefore fully
> backwards compatible.
>
>
>
> Implementation
> ==============
>
> The implementation can be found in [PR #2951][pr].
>
>
>
> Copyright
> =========
>
> This document has been placed in the public domain.
>
>
> [Elixir doc]: https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator
>     "Elixir pattern matching - the pin operator"
>
> [pr]: https://github.com/erlang/otp/pull/2951
>     "#2951: Add a new operator ^ for pinning of pattern variables"
>
>
>
> [EmacsVar]: <> "Local Variables:"
> [EmacsVar]: <> "mode: indented-text"
> [EmacsVar]: <> "indent-tabs-mode: nil"
> [EmacsVar]: <> "sentence-end-double-space: t"
> [EmacsVar]: <> "fill-column: 70"
> [EmacsVar]: <> "coding: utf-8"
> [EmacsVar]: <> "End:"
> [VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: "


--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB
Reply | Threaded
Open this post in threaded view
|

Re: New EEP draft: Pinning operator ^ in patterns

Nicolas Martyanoff
On 2021-01-14 14:13, Richard Carlsson wrote:

> The way I planned it is:
>   1. Even from the start, pinning will always be allowed, without requiring
> any flag to opt in. This does not tell you about existing uses of
> already-bound variables, but you can start using pinning right away for
> readability and for avoiding bugs when refactoring. The compiler will
> always tell you if a pinned variable doesn't exist, so you don't
> accidentally accept any value in that position.
>   2. You can enable warnings at your own pace in order to start cleaning up
> your code.
>   3. In a following major release, the warnings will be on by default, but
> you can disable them to compile old code.
>   4. In a distant future, it might become an error to not use ^ to mark
> already-bound variables.

After reading this thread, I must say this proposal makes me uneasy. One of
the things I always liked with Erlang is the simplicity and clarity of its
syntax. Matching variables by name is perfectly readable to me, and I never
had any problem of the sort refactoring code. Adding a new operator adds
noise and transforms something simple (using the same name to refer to the
same value) into something cryptic.

The fact that you envision a future where not using the operator would signal
an error is even more worrisome. I have nothing against improving the core
parts of the language (maps were a life changer for example), but this kind of
change feels really foreign to the simplicity of the Erlang syntax.

And at the risk of sounding too harsh, I would add that while I do not mind the
existence of Elixir (quite the opposite, it brought a lot of fresh air to the
entire BEAM ecosystem), I would really like Erlang to remain Erlang; in that
spirit, I see a new operator to "annotate" a perfectly clear and working
syntax as useless.

Regards,

--
Nicolas Martyanoff
http://snowsyn.net
[hidden email]
1234 ... 13