New EEP draft: Pinning operator ^ in patterns

Kostis Sagonas kostis@REDACTED
Thu Jan 21 01:09:25 CET 2021

Some days ago, I was asked (privately) to explicitly voice my opinion on 
this topic.  I've hesitated to do so, for various reasons, but FWIW here 
is my view on this EEP.

   TL;DR : Erlang will *not* become a better language with this EEP.
	(as originally described, at least.)

For those interested to read further, some more explanation follows below.

On 1/15/21 4:35 PM, Raimo Niskanen wrote:
> On Fri, Jan 15, 2021 at 04:22:55PM +0100, Nicolas Martyanoff wrote:
> :
>> I believe this discussion is moot. We can spend hours arguing about CS theory,
>> but at the end of the day, the problem is about changing a fundamental aspect
>> of a language. And clearly quite a lot of developers, me included, are worried
>> about this kind of change.
> And I want to get clarity about exactly why so many developers are worried
> about this particular proposed change, and therefore try to look thoroughly
> at the arguments.
> It is as you say a fundamental detail in the language.
> 1) Would the language be a better language with a mandatory pinning operator?
> 2) If so is there a migration path worth the trouble?
> So far I think the discussion has been centered around 2),
> before talking about 1).

Raimo raises an interesting point here.  Let's focus on 1) first.

IMO, languages do not become better by adding syntactical constructs 
which have add hoc design and, more importantly, add nothing to the 
expressive power of the language (i.e., they can be expressed with the 
constructs which already exist in the language, even if these are 
currently slightly more verbose).

I claim that this "operator" (annotation is a _much_ better word for it) 
is currently ad hoc because it is not what I would have expected from 
its design/semantics.  Its original post in this list described it as:

   The ^ operator allows you to annotate already-bound pattern variables

so, for such an operator with a consistent design, this would mean that 
a pattern as <<X:8, X:X, ...>> would be allowed, and in fact at some 
point in the future be _required_, to be written as <<X:8, ^X:^X, ...>>, 
but from what I can see/read in this EEP, this is not the case.

   (Yes, the above is ugly alright, but it's at least consistent.)

Also, I claim that, even as a compiler annotation, it does not add any 
expressive power (of the form that I would have expected, at least) 
because an annotation that _did_ add to the expressive power of pattern 
matching would have allowed programmers to control/tell the compiler the 
order that pattern matching should be performed and write clause heads 
of the form:

f(X, <<_:^X>>) ->  % Hey compiler, I'm telling you in which order to
                    % bind variables to allow me to write what I want

 From what I can see, the EEP does nothing of the above.

Now, let's focus on what the EEP actually does.  The main thing it does 
is to shut off compiler warnings when the new warning pass is enabled, 
which touches point 2) of Raimo's list, the migration path and its trouble.

But let me answer this question first:

   Should this warning pass be introduced?

My view is YES.  This pass can certainly be useful to many developers 
and I also bet it will detect real bugs in existing code bases.  So YES 
with all capital.

But IMO, we do not need strange hieroglyphics (with ad hoc design, as I 
explained above) to shut off these warnings.  Plain `when` clauses with 
=:= tests suffice for this -- perhaps with the exception of pattern 
matching with variables used as lengths of bitstring segments where the 
compiler warning pass can simply be silenced / not check.

For example, instead of changing the code:

f(X, Y) ->
     io:format("checking: ~p", [Y]),
     case X of
         {a, Y} -> {ok, Y};
         _ -> error


f(X, Y) ->
     io:format("checking: ~p", [Y]),
     case X of
         {a, ^Y} -> {ok, Y};
         _ -> error

one will need to change it to:

f(X, Y) ->
     io:format("checking: ~p", [Y]),
     case X of
         {a, FreshY} when FreshY =:= Y -> {ok, Y};
         _ -> error

and the compiler will be equally satisfied. (That's what I would be 
doing in my code, at least.)

Note that the code after the above change will be compilable not only in 
the new compiler with the warning pass enabled, but also with OLDER 
compilers that do not understand the ^ hieroglyphics.

  [So I can still use the R15B01 compiler, which is my favorite :-)]

If Richard's counting is correct and the places where such changes are 
needed are only a few --and I have no reason to doubt him-- the effort 
is equally small and the net effect is the same.  I would even argue 
that readability of the code has been increased for future readers.

Of course, the greatest advantage of the above scheme is that nobody 
would ever need to do a Google search for "Erlang carot operator" :-)

Jokes aside, this works for most case expressions, but what about Loic's 
favorite coding idiom?  (Presumably he likes to use that in test code.)

   X = 5,
   X = inc(4),
   X = dec(6),

Well, I would advice to use ?assert and friends in such situations.

The post is already too long, so I will stop here.


More information about the erlang-questions mailing list