<div dir="ltr">The way I planned it is:<div>  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.</div><div>  2. You can enable warnings at your own pace in order to start cleaning up your code.</div><div>  3. In a following major release, the warnings will be on by default, but you can disable them to compile old code.</div><div>  4. In a distant future, it might become an error to not use ^ to mark already-bound variables.</div><div><br></div><div><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature">        /Richard</div></div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Den tors 14 jan. 2021 kl 13:33 skrev Raimo Niskanen <<a href="mailto:raimo%2Berlang-questions@erlang.org">raimo+erlang-questions@erlang.org</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">As others have said: for Elixir this operator is essential, since they<br>
rebind variables without it.<br>
<br>
For Erlang, if using a pinning operator had been required from the start;<br>
I think that would have been a bit better than the current "match<br>
if already bound".  It is hard to be sure by looking at the code<br>
if the variable is already bound - you have to make a machine search.<br>
<br>
Introducing a pinning operator now is trickier...<br>
<br>
Having a compiler option to choose if pinning is allowed/required makes it<br>
hard to know what to expect from the code.  The compiler option is set in<br>
some Makefile far away from the source code.<br>
<br>
I think I would prefer that instead there should be a compiler pragma<br>
(I wish it would not be allowed from an include file but that is probably<br>
impossible to enforce) so it is visible in the current module what to<br>
expect about operator pinning.  Without the pragma the pinning operator is<br>
not allowed, with it pinning is mandatory; not a warning - an error if<br>
a pinning operator is missing.<br>
<br>
You get the idea: it should be possible from the source code how to read<br>
it, at least on the module level.<br>
<br>
How to take the next step i.e when code not using pinning is the exception,<br>
to remove the compiler pragma, I have not thought about yet...<br>
<br>
Cheers<br>
/ Raimo Niskanen<br>
<br>
<br>
<br>
On Thu, Dec 24, 2020 at 09:10:17PM +0100, Richard Carlsson wrote:<br>
> The ^ operator allows you to annotate already-bound pattern variables as<br>
> ^X, like in Elixir. This is less error prone when code is being refactored<br>
> and moved around so that variables previously new in a pattern may become<br>
> bound, or vice versa, and makes it easier for the reader to see the intent<br>
> of the code.<br>
> <br>
> See also <a href="https://github.com/erlang/otp/pull/2951" rel="noreferrer" target="_blank">https://github.com/erlang/otp/pull/2951</a><br>
> <br>
> Ho ho ho,<br>
> <br>
>         /Richard & the good folks at WhatsApp<br>
<br>
>     Author: Richard carlsson <carlsson.richard(at)gmail(dot)com><br>
>     Status: Draft<br>
>     Type: Standards Track<br>
>     Created: 21-Dec-2020<br>
>     Erlang-Version: 24<br>
>     Post-History: 24-Dec-2020<br>
> ****<br>
> EEP XXX: Pinning operator ^ in patterns<br>
> ----<br>
> <br>
> <br>
> Abstract<br>
> ========<br>
> <br>
> This EEP proposes the addition of a new unary operator `^` for<br>
> explicitly marking variables in patterns as being already bound.  This<br>
> is known as "pinning" in Elixir - see [Elixir doc][the Elixir<br>
> documentation].<br>
> <br>
> For example:<br>
> <br>
>     f(X, Y) -><br>
>         case X of<br>
>             {a, Y} -> ok;<br>
>             _ -> error<br>
>         end.<br>
> <br>
> could be written more explicitly:<br>
> <br>
>     f(X, Y) -><br>
>         case X of<br>
>             {a, ^Y} -> ok;<br>
>             _ -> error<br>
>         end.<br>
> <br>
> In Elixir, this operator is strictly necessary for being able to refer<br>
> to the value of a bound variable as part of a pattern, because<br>
> variables in patterns are always regarded as being new shadowing<br>
> instances (like in Erlang's fun clause heads), unless explicitly<br>
> pinned.<br>
> <br>
> In Erlang, they would be optional, but are still a good idea because<br>
> they make programs more robust under edits and refactorings, and<br>
> furthermore allow the use of pinned variables in fun clause heads and<br>
> in comprehension generator patterns.<br>
> <br>
> <br>
> Specification<br>
> =============<br>
> <br>
> A new unary operator `^` is added to Erlang, called the "pinning<br>
> operator".  It may only be used in patterns, and only on variables.<br>
> Its meaning is that the "pinned" variable is to be interpreted in the<br>
> enclosing environment of the pattern, and its value used in its place<br>
> for that position in the pattern.<br>
> <br>
> In current Erlang, this behaviour is what happens automatically in<br>
> ordinary matching constructs if the variable is already bound in the<br>
> enclosing environment.  In the following example:<br>
> <br>
>     f(X, Y) -><br>
>         case X of<br>
>             {a, Y} -> {ok, Y};<br>
>             _ -> error<br>
>         end.<br>
> <br>
> the use of `Y` in the pattern is regarded as a reference to the<br>
> function parameter `Y`, instead of as introducing a new variable, and<br>
> the `Y` in the clause body is then that same parameter.  Therefore,<br>
> annotating the pattern variable as `^Y` in this case does not change<br>
> the behaviour of the program, but makes the intent explicit:<br>
> <br>
>     f(X, Y) -><br>
>         case X of<br>
>             {a, ^Y} -> {ok, Y};<br>
>             _ -> error<br>
>         end.<br>
> <br>
> For fun expressions and list comprehension generator patterns, the<br>
> pinning operator makes the language more expressive.  Take the<br>
> following Erlang code:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, Y}) -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> Here, the occurrence of `Y` in the clause head of the fun `F` is a new<br>
> variable instance, shadowing the `Y` parameter of `f(X, Y)`, and the<br>
> fun clause will match any value in that position.  The `Y` in the<br>
> clause body is the one bound in the clause head.  However, using the<br>
> pinning operator, we can selectively match on variables bound in the<br>
> outer scope:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, ^Y})  -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> In this case, there is no new binding of `Y`, and the use of `Y` in<br>
> the fun clause body refers to the function parameter.  But it is also<br>
> possible to combine pinning and shadowing in the same pattern:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, ^Y, Y})  -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> In this case, the pinned field refers to the value of the function<br>
> function parameter, but there is also a new shadowing binding of `Y`<br>
> to the third field of the tuple.  The use in the fun clause body now<br>
> refers to the shadowing instance.<br>
> <br>
> Generator patterns in list comprehensions or binary comprehensions<br>
> follow the same rules as fun clause heads, so with pinning we can for<br>
> example write the following code:<br>
> <br>
>     f(X, Y) -><br>
>         [{b, Y} || {a, ^Y, Y} <- X].<br>
> <br>
> where the `Y` in `{b, Y}` is the shadowing instance bound to the third<br>
> element of the pattern tuple.<br>
> <br>
> Finally, a new compiler flag `warn_unpinned_vars` is added, disabled<br>
> by default, which if enabled makes the compiler emit warnings about<br>
> all uses of already bound variables in patterns that are not<br>
> explicitly annotated with the `^` operator.  This allows users to<br>
> migrate their code module by module towards using explicit pinning in<br>
> all their code.  If pinning becomes the norm in Erlang, this flag<br>
> could be turned on by default, and eventually, the pinning operator<br>
> could become strictly required for referring to already bound<br>
> variables in patterns.<br>
> <br>
> <br>
> Rationale<br>
> =========<br>
> <br>
> The explicit pinning of variables in patterns make programs more<br>
> readable, because the intent of the code becomes clear.  When already<br>
> bound variables are used in Erlang without any annotation, anyone<br>
> reading a piece of code must first study it closely to understand<br>
> which variables will be bound at the point of a pattern, before they<br>
> can tell whether any pattern variable is a new binding or implies an<br>
> equality assertion.  This is easy to miss even for experienced<br>
> Erlangers, be it during code reviews or while trying to understand a<br>
> piece of poorly commented code.<br>
> <br>
> Perhaps more importantly, pinning also makes programs more robust<br>
> under edits and refactorings.  Take our previous example, and add a<br>
> print statement:<br>
> <br>
>     f(X, Y) -><br>
>         io:format("checking: ~p", [Y]),<br>
>         case X of<br>
>             {a, Y} -> {ok, Y};<br>
>             _ -> error<br>
>         end.<br>
> <br>
> Suppose someone renames the function parameter from `Y` to `Z` and<br>
> updates the print statement but forgets to update the use in the case<br>
> clause.  Without an explicit pinning annotation, the change would be<br>
> quietly allowed, but the `Y` in the pattern would be interpreted as a<br>
> new variable that will match any value, which will then be used in the<br>
> body.  This changes the behaviour of the program.  If the use in the<br>
> pattern had been annotated as `^Y`, the compiler would have generated<br>
> an error "Y is unbound" and the mistake would have been caught.<br>
> <br>
> When code is being modified to add a feature or fix a bug, a<br>
> programmer might want to introduce a new variable for a temporary<br>
> result.  In a long function body, this risks introducing a new bug.<br>
> Consider the following:<br>
> <br>
>     g(Stuff) -><br>
>        ...<br>
>        Thing = case ... of<br>
>                    {a, T} -> T;<br>
>                    _ -> 0<br>
>                end,<br>
>        ...<br>
>        {ok, [Thing|Stuff]}.<br>
> <br>
> Here, `T` is a new variable, clearly intended as just a temporary and<br>
> local variable for extracting the second element of the tuple.  But<br>
> suppose that someone adds a binding of the name `T` further up in the<br>
> function body, without noticing that the name is already in use:<br>
> <br>
>     g(Stuff) -><br>
>        ...<br>
>        T = q(Stuff) + 1,<br>
>        io:format("~p", [p(T)]),<br>
>        ...<br>
>        Thing = case ... of<br>
>                    {a, T} -> T;<br>
>                    _ -> 0<br>
>                end,<br>
>        ...<br>
>        {ok, [Thing|Stuff]}.<br>
> <br>
> Now the first clause of the case switch will only match if the second<br>
> element of the tuple has the exact same value as the previously<br>
> defined `T`.  Again, the compiler quietly accepts this change, while<br>
> if it had been instructed to warn about all non-annotated uses of<br>
> already bound variables in patterns, this mistake would have been<br>
> detected.<br>
> <br>
> <br>
> Shadowing in Funs and Comprehensions<br>
> ------------------------------------<br>
> <br>
> In funs and comprehensions, pinning also lets us do things that<br>
> otherwise requires additional temporary variables.  Consider the<br>
> following code:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, Y}) -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> Since the `Y` in the clause head of the fun is a new shadowing<br>
> instance, the pattern will match any value in that position.  To match<br>
> only the value passed as `Y` to `f`, a clause guard must be added, and<br>
> a temporary variable be used to access the outer `Y`:<br>
> <br>
>     f(X, Y) -><br>
>         OuterY = Y,<br>
>         F = fun ({a, Y}) when Y =:= OuterY -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> We could instead rename the inner use of `Y` to avoid shadowing, but<br>
> the equality test must still be written as an explicit guard:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, Z}) when Z =:= Y -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> With the help of the pinning operator, such things are no longer a<br>
> concern, and we can simply write:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, ^Y}) -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> Furthermore, in the odd case that the pattern would both need to<br>
> access the surrounding definition of `Y` as well as introduce a new<br>
> shadowing binding, this can be easily written using pinning:<br>
> <br>
>     f(X, Y) -><br>
>         F = fun ({a, ^Y, Y})  -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> but in current Erlang, two separate temporary variables would be<br>
> required:<br>
> <br>
>     f(X, Y) -><br>
>         OuterY = Y,<br>
>         F = fun ({a, Temp, Y}) when Temp =:= OuterY -> {ok, Y};<br>
>                 (_) -> error<br>
>             end,<br>
>         F(X).<br>
> <br>
> As explained before, the same goes for patterns in generators of<br>
> comprehensions.<br>
> <br>
> <br>
> <br>
> Backwards Compatibility<br>
> =======================<br>
> <br>
> The addition of a new and previously unused operator `^` does not<br>
> affect the meaning of existing code, and the compiler will not emit<br>
> any new warnings or errors for existing code, unless explicitly<br>
> enabled with `warn_unpinned_vars`.  This change is therefore fully<br>
> backwards compatible.<br>
> <br>
> <br>
> <br>
> Implementation<br>
> ==============<br>
> <br>
> The implementation can be found in [PR #2951][pr].<br>
> <br>
> <br>
> <br>
> Copyright<br>
> =========<br>
> <br>
> This document has been placed in the public domain.<br>
> <br>
> <br>
> [Elixir doc]: <a href="https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator" rel="noreferrer" target="_blank">https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator</a><br>
>     "Elixir pattern matching - the pin operator"<br>
> <br>
> [pr]: <a href="https://github.com/erlang/otp/pull/2951" rel="noreferrer" target="_blank">https://github.com/erlang/otp/pull/2951</a><br>
>     "#2951: Add a new operator ^ for pinning of pattern variables"<br>
> <br>
> <br>
> <br>
> [EmacsVar]: <> "Local Variables:"<br>
> [EmacsVar]: <> "mode: indented-text"<br>
> [EmacsVar]: <> "indent-tabs-mode: nil"<br>
> [EmacsVar]: <> "sentence-end-double-space: t"<br>
> [EmacsVar]: <> "fill-column: 70"<br>
> [EmacsVar]: <> "coding: utf-8"<br>
> [EmacsVar]: <> "End:"<br>
> [VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: "<br>
<br>
<br>
-- <br>
<br>
/ Raimo Niskanen, Erlang/OTP, Ericsson AB<br>
</blockquote></div>