Variable rebinding by default seems problematic

371 views
Skip to first unread message

Chris Farmiloe

unread,
Dec 8, 2013, 8:57:36 AM12/8/13
to elixir-l...@googlegroups.com

Hi.. really enjoying elixir so far, but having variable name rebinding by default looks like it's going to be a major source for bugs!

Just to be clear; I like that we can rebind variable names, my concern is that having it as the default behaviour is asking for trouble.

It's going to be a very easy mistake to miss off a hat (^) when doing pattern matching (in comparison to accidently adding one in), and these mistakes are going to go unnoticed at runtime.

Is everyone ok with this?

Since the language is still young, it seems like something that could and should be addressed to me, before it's too late, and people are spending hours searching for a missing hat.

One way to solve this would be to invert the use of ^ ... so that it means "allow me to be reassigned"..... personally I think being explicit like this would be preferable, but obviously many people may like this behaviour.

Another way could be to give some kind of warning by default if you reassign an existing binding (at compile time), and have a new operator (sayyyy ":=" ) that means "I know what I'm doing shut up compiler"

So,

    a = 1   # everything fine, a was previously unbound
    a = 2   # generates a warning, but binds 2 to a anyway
    a := 2  # no warning, binds 2 to a

This method would be backwards compatible with the current system, while also making it much much easier to track down errors due to accidentally missing hats.

Thoughts?




meh.

unread,
Dec 8, 2013, 9:01:34 AM12/8/13
to elixir-l...@googlegroups.com
On Sun, Dec 08, 2013 at 05:57:36AM -0800, Chris Farmiloe wrote:
> Thoughts?

In the past year of using Elixir, this kind of bug never happened to
me.

José Valim

unread,
Dec 8, 2013, 9:16:37 AM12/8/13
to elixir-l...@googlegroups.com
It's going to be a very easy mistake to miss off a hat (^) when doing pattern matching (in comparison to accidently adding one in), and these mistakes are going to go unnoticed at runtime.

That depends a lot on your background. If you are coming from Erlang, it may happen while adapting to Elixir, but it hasn't happened to me in practice. If you are coming from another language, you wouldn't expect a match to happen anyway, so one would automatically associated that with the hat operator.

Interesting enough, three days ago I was having dinner with Joe Armstrong, one of Erlang creators, and he told me one of his favo(u)rite features in Elixir is exactly the ^ operator. He likes that pattern matching on a previous value is made explicit, rather than implicit, as it makes it easier to read the code. Otherwise you need to keep all bound variables in your head in order to know if in a given match a variable is being bound or matched on. His words. :)

On a similar note, the implicit behaviour also makes code refactoring harder, because removing a variable would remove the match, but the code will still compile.

Chris Farmiloe

unread,
Dec 8, 2013, 9:36:17 AM12/8/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br

 
I was having dinner with Joe Armstrong,

Hehe... well I'll concede to wisdom!

I guess, my concerns could be alleviated if there was some kind of external analysis tool / debugger that might help point us in the right direction and flag common potential pitfalls (like unexpected reassignment during a patten match). In fact that sounds like quite a fun project if someone hasn't started one already.

Exilir seems much more forgiving than erlang, which is fantastic for productivity, but sometimes, being the masochist that I am I like being told off by compilers :)

Peter Minten

unread,
Dec 8, 2013, 9:48:36 AM12/8/13
to elixir-l...@googlegroups.com
On 12/08/2013 03:36 PM, Chris Farmiloe wrote:
>
>
>
>> I was having dinner with Joe Armstrong,
>>
>
> Hehe... well I'll concede to wisdom!
>
> I guess, my concerns could be alleviated if there was some kind of external
> analysis tool / debugger that might help point us in the right direction
> and flag common potential pitfalls (like unexpected reassignment during a
> patten match). In fact that sounds like quite a fun project if someone
> hasn't started one already.

Given how easy to work with the AST is that should be quite doable. I
haven't heard of any such project yet.

Greetings,

Peter

Arie van Wingerden

unread,
Jun 15, 2014, 7:08:00 AM6/15/14
to elixir-l...@googlegroups.com
Hi Chris,

having looked at Elixir I find it a very well designed language, except....

Reassignment bothers me very strongly, especially because it is the default.
I like Chris' idea to have a := operator, whilst having pattern matching as the default.

Nobody else with the same fear / trouble?

Kind regards (ans José: thanks for this wonderful language),
   Arie

Op zondag 8 december 2013 14:57:36 UTC+1 schreef Chris Farmiloe:

José Valim

unread,
Jun 15, 2014, 8:49:19 AM6/15/14
to elixir-l...@googlegroups.com
Hello Arie, thanks for the nice words.

I will post the same answer from before: I like the explicitness of pattern matching. In Elixir, as soon as I see ^foo, I know I am matching. If there is no ^, I know the previous value regardless if it there is one or not, will be discarded. If pattern matching is not explicit, I always need to know if a variable was previously defined or not to know what is going to happen. To me this behaviour is non negotiable. In my experience, it is more likely to run into accidental matches than into accidental rebindings.

Another possible limitation to the suggestion above can be related to macros. Let's suppose you have a macro that stores a value in a hygienic variable:

defmacro do_something(a, b) do
  quote do
    var!(hello, Hygienic) = calculate_something(unquote(a), unquote(b))
  end
end

Someone may call this macro multiple times but we always care about the last value of hello. If we make a distinction in between being assigned once and then multiple times, the macro simply won't work. Or the macro developer would need to work around this behaviour by inspecting the environment or we would need to provide some sort of functionality that does it for you. Maybe this behaviour could be built-in in var! or maybe we'd need to introduce something like:

defmacro do_something(a, b) do
  quote do
    set!(hello, Hygienic) = calculate_something(unquote(a), unquote(b))
  end
end

We can see how this is getting complex:

1. Use = to define variables
2. Use ^ for pattern matching
3. Use := for rebinding
4. Use set! in macros when you don't care if a variable was previously defined or not

It is also interesting to point out that the := operator won't work for rebinding inside clauses. For example, how would you make the rebinding below explicit?

x = true
case false do
  x -> :ok
end

Of course, there are interesting consequences for making a distinction in between defining and rebinding a variable by introducing something like the := operator. We could have better control of the scope to provide better warnings or even make it easier to implement imperative for loops:

x = 0

for i <- 1..5 do
  x := x + i
end

x #=> 15

But the fact Elixir have different ways for variables to be introduced in the scope, adding more rules can make the overall system very complex.




José Valim
Skype: jv.ptec
Founder and Lead Developer


--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Arie van Wingerden

unread,
Jun 15, 2014, 9:12:29 AM6/15/14
to elixir-l...@googlegroups.com
Hi José,

if nobody else is having the problem, well that's than that ;-)
And the fact that even Joe likes this as a feature has a lot to say...

I am a huge fan of "least surprise" syntax and maybe it is only *my* surprise being used to the strict Erlang way variables are handled.

Of course I highly respect all the "blood, sweat and tears" invested in Elixir and I do understand your points against changing this behavior very clearly.

So, I guess I have to adjust myself and to try to even *like* this as a well thought out feature. Alas, I will surely make quite a few mistakes with it in the beginning ;-)

Anyway, thank you very much for this great language. I hope it will be of great help to increase Erlang's popularity and usage.

Kind regards and wishing you the best,
   Arie

José Valim

unread,
Jun 15, 2014, 10:16:05 AM6/15/14
to elixir-l...@googlegroups.com
Thanks Arie,

To be clear, there may be a solution to the problems being discussed we have glanced over. The point of my previous e-mail is exactly to highlight all the trade-offs done so far so everyone can make a well-informed decision or even suggest a proposal that fits all criteria.

Cheers!



José Valim
Skype: jv.ptec
Founder and Lead Developer


Dave Martin

unread,
Jun 16, 2014, 12:56:04 PM6/16/14
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
For what its worth, I did just recently make this mistake, and it seems a bit ironic considering most of my background is from non-functional languages:

I said:
      def wait_for_port(port) do
        receive do
          {port, {:exit_status, status}} -> status

but I meant:
      def wait_for_port(port) do
        receive do
          {^port, {:exit_status, status}} -> status

The program works either way (until something not matching port happens, which I'm not expecting in this particular program, so this would have gone undetected). Fortunately, it generated a warning (in this simple case) that port was unused.

I think I made the mistake because I was thinking of the clause as a pattern match, that would only bind unknown things, One of the hazards of being multi-lingual I suppose. While I like being able reuse references, It did make me appreciate the Erlang philosophy of always knowing what a variable represents (which I appreciated before, but this was a reminder).

So I lean a bit toward the ^ means rebind is ok version (and absence of ^ means it acts like an Erlang "variable"). But others have given this way more thought than I have, I'm sure.

Eric Meadows-Jönsson

unread,
Jun 16, 2014, 1:03:07 PM6/16/14
to elixir-l...@googlegroups.com

You can just as well get bugs for the opposite reason.

You bind a variable somewhere above in your code and then assign to the variable without knowing it was already bound.

Example Erlang code:

foo() ->
  Var = 123,
  % lots of code here
  % ...

  case expression() of
    % oops, hidden bug, I forget Var was already bound in the code above
    Var -> ...
  end.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Eric Meadows-Jönsson

Robert Virding

unread,
Jun 16, 2014, 5:37:15 PM6/16/14
to elixir-l...@googlegroups.com
Whatever your preference the Erlang way is a little more consistent, there is only one variable with a name and it only has one value in a function body. And yes I know it causes a bit of extra work to name new variables, Foo0, Foo1, Foo2, etc.

And saying that you are not really changing the value of a variable just pointing the name to another variable doesn't hold in erlang/elixir, as there really are no variables in the classical imperative sense of a hole where you can put a value. A variable is just a reference that exists in code to data. When you write x = foo(42) you are not creating a variable x in memory containing the value of foo(42).

Anyway it is not going to change now, and whichever choice you make some will like it and some won't. :-)

Robert

Devin Torres

unread,
Jun 19, 2014, 4:48:43 AM6/19/14
to elixir-l...@googlegroups.com
Been programming Elixir professionally for a year now, and variable rebinding has never once caused the much fabled and oft maligned "I accidentally rebound something I didn't intend to!" scenario. I think it's a fairy tale that was invented as part of some justification for not allowing it way back in the day and maybe sounded like something slightly plausible. When I rebind names, it's because I intended it. Your functions should never be more than few lines long in the first place.

Devin Torres

unread,
Jun 19, 2014, 4:52:42 AM6/19/14
to elixir-l...@googlegroups.com
In fact, rebinding names is one of the many things that made me fall in love with Elixir in the first place oh so long ago. I love that Elixir avoids being dogmatic and tries to be pragmatic when it makes sense.

Michael Brodhead

unread,
Jun 20, 2014, 5:47:14 PM6/20/14
to elixir-l...@googlegroups.com
I like the idea that I can rebind variables but was surprised that is the default behavior. Aside from breaking existing code, what are the downsides of simply reversing the meaning of ^ while leaving everything else unchanged? That is "a = 1" only performs a pattern match while "^a = 1" potentially rebinds. That way the language guides us toward a purer approach while still providing an easy way to break out any time we need to.

I have been really enjoying Elixir so far. I haven't been this excited about a programming language for a ling time.

--mkb

Reply all
Reply to author
Forward
0 new messages