I think the new 1.4 warning about missing () on zero arity functions is a cure that's worse than the disease

1,144 views
Skip to first unread message

Dave Thomas

unread,
Dec 2, 2016, 10:42:45 PM12/2/16
to elixir-lang-core

I’ve been loving 1.4 for a few weeks now, but I am bugged by the new warning:

 warning: variable "int" does not exist and is being expanded to "int()", please use parentheses to 
   remove the ambiguity or change the variable name

Partly it is because it makes my code a lot uglier. For example, in quixir, instead of

test "two plain types" do 
  ptest a: int, b: list do
   ​ assert is_integer(a) 
    assert is_list(b) 
  end 
end

I now have to write:

test "two plain types" do
  ptest a: int(), b: list() do
   . . .

Ugh. Even worse, the premise of the warning seems wrong. It assumes int is a variable, which doesn’t exist. But it does know that int is a function, because if I misspell it and put ()s on, I get a compilation error. So why can’t it just do that: is a bare name is encountered that isn’t a variable, just internally tack on the ()s are see what happens. Which I think is the old way.

Basically, what compelling problem drove this change? It isn’t an issue I ever had before, and the change seems to make my code worse.

Dave

Louis Pilfold

unread,
Dec 3, 2016, 1:45:45 AM12/3/16
to elixir-lang-core
I personally think this is clearer, and I don't feel this is clearer. With this I think it is easier to tell when computation is happening- previously it could be hidden behind what looks like variables.

Cheers,
Louis


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/22ad983c-0917-4425-abbe-a6e954ae3b61%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Amos King

unread,
Dec 3, 2016, 8:10:17 AM12/3/16
to elixir-l...@googlegroups.com
I prefer being able to use the bare words. It makes refactoring simpler to move between a variable and a method which I do regularly.  The warning seems strange since there isn't an ambiguity that is being decided. I'm on board with Dave here. 

Amos King
Binary Noggin

Michał Muskała

unread,
Dec 3, 2016, 8:50:40 AM12/3/16
to elixir-l...@googlegroups.com
The ambiguity, that bit me already several times is in the code that looks like this:

self = self()
do_something(self)

Now the self in the second line can be both - calling the function and accessing variable. Of course it's not confusing in this example because of it's brevity, but in general it can lead to issues that are very hard to debug.

Michał.
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/1865E0E4-7879-4307-900B-99CEF366091D%40binarynoggin.com.

Amos King

unread,
Dec 3, 2016, 9:07:25 AM12/3/16
to elixir-l...@googlegroups.com
In my experience the minimize scope of data binding keeps it pretty straightforward.

Amos King
Binary Noggin
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/1D0E5277-451A-4955-A3FC-D06F05E107E4%40muskala.eu.

Allen Madsen

unread,
Dec 3, 2016, 9:34:39 AM12/3/16
to elixir-l...@googlegroups.com
It looks like this was already discussed here:
https://github.com/elixir-lang/elixir/issues/3268
https://github.com/elixir-lang/elixir/pull/3517

I prefer bare words for the same reason as Amos. It facilitates
refactoring between methods and variables.

If this stays, is it expected to be a compiler error in Elixir 2.0? If
so, does that mean that we would be able to call anonymous functions
without the dot?

x = fn y -> y + 1 end
x(1)
# vs
x.(1)
Allen Madsen
http://www.allenmadsen.com
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/15857D16-3830-4916-B233-2CD929719BDF%40binarynoggin.com.

Amos King

unread,
Dec 3, 2016, 10:12:53 AM12/3/16
to elixir-l...@googlegroups.com
I understand that the anonymous function is both a value and a function which is why we have to call it with parentheses. I don't understand why it needs the dot. Seems the only time that would become confusing is if you had a first class function and an Anonymous function with the same name.

I'm In favor of the one caveat of the anonymous functions needing to have the parentheses and a dot in favor of being able to use bare words.
Amos King
Binary Noggin
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK-y3Csyb3ox0pGKCBuTEzK67%2BTcZGwJzx5VphWr9_4pNhwi8g%40mail.gmail.com.

Amos King

unread,
Dec 3, 2016, 10:20:22 AM12/3/16
to elixir-l...@googlegroups.com
Actually could this be solved externally to the language with a static code analysis tool?

Amos King
Binary Noggin

> On Dec 3, 2016, at 08:34, Allen Madsen <allen.c...@gmail.com> wrote:
>
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK-y3Csyb3ox0pGKCBuTEzK67%2BTcZGwJzx5VphWr9_4pNhwi8g%40mail.gmail.com.

José Valim

unread,
Dec 3, 2016, 10:53:10 AM12/3/16
to elixir-l...@googlegroups.com
If this stays, is it expected to be a compiler error in Elixir 2.0?

That's still being discussed as we evaluate the overall strategy of what is a warning and what is an error.

> If so, does that mean that we would be able to call anonymous functions
without the dot?

No because the anonymous function call without the dot is not an issue to how the function is invoked but related to how a function is captured. This has been discussed extensively in the past so I recommend looking at those previous discussions, especially the one that explains Lisp 1 vs Lisp 2 namespaces.


José Valim
Skype: jv.ptec
Founder and Director of R&D

José Valim

unread,
Dec 3, 2016, 11:12:30 AM12/3/16
to elixir-l...@googlegroups.com
Basically, what compelling problem drove this change? It isn't an issue I ever had before, and the change seems to make my code worse.

There were a couple scenarios that I either personally ran into or were frequently reported. One of those came from Plug test suite. In Plug it is common to pass the connection named as "conn" around between setup/tests:

setup do
  {:ok, conn: conn(:get, "/foo/bar")}
end

test "can do a get request" do
  assert SomePlug.call(conn, []).path_into == ["foo", "bar"]
end

There is a bug in the code above in that we should be extracting the "conn: conn" variable out of the context but I forgot to. However, since Plug.Test defines a conn/0 function, a connection would be created automatically on the SomePlug.call(conn, []) call but without the HTTP method and path I have set in setup. This usually leads to very frustrating debugging sessions because the error above is not obvious. You will very likely first debug SomePlug.call, then figure out the path was never set to "/foo/bar", which only then will lead you to the root cause.

The opposite also happens. For example I may start a property testing like this:

int = 42
ptest list: list(length: int, of: atom) do
  assert ...
end

Then later I come back to the code, decide that my list can be made of atoms and ints, and rewrite it as follows:

ptest list: list(length: int, of: choose(atom, int)) do
  assert ...
end

which will now fail due to the previous int = 42 line.

A lot of it boils down to how much of the surrounding scope you need to parse in order to do a change to the codebase. Having variables automatically upgrading to function calls means that, if you ever want to call a zero-function arity without parens, you need to check all variables being defined.

While some may have never experienced this issue, it has affected many other developers, including myself, and I would have appreciated more help from the compiler.

It is very similar to the deprecation we did on Elixir v1.2 (or v1.3) about imperative assignment. While there was no ambiguity in imperative assignment, the fact the feature existed meant that in order to understand the value of a variable at certain piece of code, I had to traverse all nested expressions in the chance a variable was defined inside an if that was inside a case.

By forbidding such constructs, we need to keep less context in our brain when reading or changing the code. And it is important to understand this ability changes from person to person. For instance, some developers are very comfortable with "import Foo" while others do not appreciate it as it means you need to have an understand of everything Foo brings into the current scope.





José Valim
Skype: jv.ptec
Founder and Director of R&D

On Sat, Dec 3, 2016 at 4:36 AM, Dave Thomas <da...@pragdave.me> wrote:
I've been living with 1.4 for a few weeks now. 

I am bugged by the new warning:

~~~
warning: variable "int" does not exist and is being expanded to "int()", please use parentheses to
 remove the ambiguity or change the variable name                                                
  test/generators/int_test.exs:10
~~~

Partly it is because it makes my code a lot uglier. For example, in quixir, instead of 

~~~elixir
  test "two plain types" do
    ptest a: int, b: list do
      assert is_integer(a)
      assert is_list(b)
    end
  end
~~~

I now have to write:
~~~ elixir
  test "two plain types" do
    ptest a: int(), b: list() do
      assert is_integer(a)
      assert is_list(b)
    end
  end
~~~

Ugh.

Even worse, the premise of the warning seems wrong. It assumes `int` is a variable, which doesn't exist. But it _does_ know that `int` is a function, because if I misspell it and put ()s on, I get a compilation error. So why can't it just do that: is a bare name is encountered that isn't a variable, just internally tack on the ()s are see what happens. Which I think is the old way.

Basically, what compelling problem drove this change? It isn't an issue I ever had before, and the change seems to make my code worse.


Dave

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/924b70db-79f1-43c1-afc7-49a94cd541e5%40googlegroups.com.

José Valim

unread,
Dec 3, 2016, 11:14:41 AM12/3/16
to elixir-l...@googlegroups.com
Btw Dave, it is really nice to see you are exploring a property testing library. When Andrea proposed for one to be introduced in Elixir, I said we should explore the ability of building generators as streams and that's exactly what you did with pollution. I loved "when factories meet streams". I hope to play with this relatively soon. :)

Steve Pallen

unread,
Dec 3, 2016, 1:32:25 PM12/3/16
to elixir-lang-core
I'm dead against this warning. Sure, I've tripped over this issue a few times, but the benefit of this change is not worth the cost IMHO. I'll have to update all my commercial projects, my open source projects, and likely fork/submit PRs on some of my 3rd party dependencies. Until all that is done, I'll have numerous compiler warnings generated which is a distraction from noticing "real issues" warnings. Without the ability to disable specific warnings in the compiler, I think this warning is a terrible idea.


José Valim

unread,
Dec 3, 2016, 3:07:00 PM12/3/16
to elixir-l...@googlegroups.com
The warning was first meant to be introduced on Elixir v1.3 but we have delayed it because we had other warnings introduced in that version, such as the xref ones, and we wanted to call attention to those.

On this version we have focused mainly on the variable warnings because we understand they may be too frequent in some projects and also to avoid distracting from more important ones, as you said. Given Mix only compiles the files you changed, after the initial compilation, you should be able to tackle warnings gradually.

If there are other warnings on this release besides the variable one that you believe are getting in the way, we will gladly hold them for another version so everyone is able to take their time to work on their projects.

I don't want to make warnings optional. As a consequence, we are trying hard to not overload your projects with warnings on new versions. If that's happening, we are glad to discuss better strategies (for example, we can initially emit warnings only for variables inside functions) but overall the warning is not going away.




José Valim
Skype: jv.ptec
Founder and Director of R&D

On Sat, Dec 3, 2016 at 7:32 PM, Steve Pallen <smpal...@gmail.com> wrote:
I'm dead against this warning. Sure, I've tripped over this issue a few times, but the benefit of this change is not worth the cost IMHO. I'll have to update all my commercial projects, my open source projects, and likely fork/submit PRs on some of my 3rd party dependencies. Until all that is done, I'll have numerous compiler warnings generated which is a distraction from noticing "real issues" warnings. Without the ability to disable specific warnings in the compiler, I think this warning is a terrible idea.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

Dave Thomas

unread,
Dec 4, 2016, 12:36:24 PM12/4/16
to elixir-lang-core, jose....@plataformatec.com.br

On Saturday, December 3, 2016 at 10:12:30 AM UTC-6, José Valim wrote:

Basically, what compelling problem drove this change? It isn't an issue I ever had before, and the change seems to make my code worse.

There were a couple scenarios that I either personally ran into or were frequently reported. One of those came from Plug test suite. In Plug it is common to pass the connection named as "conn" around between setup/tests:

So we lose things like this?

iex(2)> v
warning: variable "v" does not exist and is being expanded to "v()", please use parentheses to remove the ambiguity or change the variable name                                                      
  iex:2

There’s a simple cure for variable/function ambiguity—choose good names and write trivial functions. This kind of thing typically bites when your code is struggling in some extended swamp of names. Maybe being bitten on those occasions is just what the developer needs to stop doing it :)

Even if you really, really want to do this (please reconsider), could we at least not warn if the ambiguity can be resolved at compile time—if there’s a function/0 available with the given name, just compile it as a call and move on?

Dave

José Valim

unread,
Dec 4, 2016, 1:11:06 PM12/4/16
to Dave Thomas, elixir-lang-core
could we at least not warn if the ambiguity can be resolved at compile time—if there’s a function/0 available with the given name, just compile it as a call and move on?

That's exactly the behaviour we had before. After all, if you do not have a variable nor a function name, it wouldn't compile with reason no variable or function named x.


José Valim
Skype: jv.ptec
Founder and Director of R&D

Peter Hamilton

unread,
Dec 4, 2016, 1:43:47 PM12/4/16
to elixir-l...@googlegroups.com, Dave Thomas

Could we warn only if both a function and a var exist, as in the Plug test case?

I think the narrowest warning that would still happen on your examples of bad behavior is where we should start.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4KPq2UyFF2qP-FwFAagG-KCoKam_GfHgwztC0sm9j3NgQ%40mail.gmail.com.

Dave Thomas

unread,
Dec 4, 2016, 1:46:59 PM12/4/16
to elixir-lang-core, da...@pragdave.me, jose....@plataformatec.com.br


On Sunday, December 4, 2016 at 12:11:06 PM UTC-6, José Valim wrote:
could we at least not warn if the ambiguity can be resolved at compile time—if there’s a function/0 available with the given name, just compile it as a call and move on?

That's exactly the behaviour we had before. After all, if you do not have a variable nor a function name, it wouldn't compile with reason no variable or function named x.

yup :)

Is it possible to detect if a variable shadows a function, and warn in that case?  I really, really, REALLY don't want to write v() in iex... :)


Dave 

José Valim

unread,
Dec 4, 2016, 5:03:39 PM12/4/16
to Dave Thomas, elixir-lang-core
Is it possible to detect if a variable shadows a function, and warn in that case?  I really, really, REALLY don't want to write v() in iex... :)

Oh, I understand it now.

So going back to the cases the current warning is meant to solve that I sent in an earlier email, warning only if it shadows a function, does not solve the first case although it does also solve the second case.

However, it does not solve the contextual overhead, When changing or reading code, I still need to carefully look at the surrounding context to see if the variable is not being used in order to avoid the warning (or I can write the code and wait for the compiler to tell me).

But to make things worse, the suggestion of only warning if there is a function, means that by simply adding a function into a given module, you may now get warnings on other functions in that same module, simply because you defined a new function 100LOC below. It is even worse if you consider imports: if you are importing or using an external library and it adds a new function to its API, you may have warnings when updating your dependency.

Those are really undesired consequences. I believe we should either define that one of them has higher precedence (Elixir v1.3) or make sure they don't conflict (Elixir v1.4).

I really, really, REALLY don't want to write v() in iex... :)

We are actually capable of avoiding the warning by detecting those special variables. In fact, the same could be done in the ptest macro you posted earlier (I believe it is a macro?). You can traverse the AST, looking for variables and if there is no actual variable (check both __CALLER__.vars and __CALLER__.export_vars), then you can upgrade to a call before it reaches the Elixir compiler. That's why "foo |> bar" does not warn, because it bar becomes bar/1 before the compiler parses the code.

I will open up an issue to discuss if we should do so in IEx helpers.

Dave Thomas

unread,
Dec 4, 2016, 6:10:17 PM12/4/16
to elixir-lang-core, da...@pragdave.me, jose....@plataformatec.com.br

But if it is desirable in iex, and acceptable in pipelines, why not other code?

I just don’t get why this is such a problem. Having

setup do
  {:ok, conn: conn(:get, "/foo/bar")}
end

seems to be just bad naming. They are deliberately choosing to have a key with the same name as a function. That seems to be confusing in its own right. If this causes a test to fail, I think that’s a good thing, as it might spur the writer to rethink the naming.

I think the same thing applies with

int = 42
ptest list: list(length: int, of: int) do  
 assert ...
end

All the arguments I’ve seen so far have nothing to do with function/variable ambiguity. They are simply to do with name clashes. They are no different to

size = 42

# some code ...

size = &String.length/1

ptest list: list(length: size, of: choose(atom, int)) do
  assert ...
end

Both this and the function/variable case fail because a name was used twice. It’s a case of Caveat Coder.



Dave

José Valim

unread,
Dec 4, 2016, 7:01:07 PM12/4/16
to Dave Thomas, elixir-lang-core
It is not about being desirable for pipeline, I only explained why there isn't a warning there and why we didn't have to change the pipe to not get a warning. It just worked which makes perfect sense because there is no ambiguity there.

And name clash is the ambiguity. It is only unclear what they mean because the names can clash. And I agree coming up with better names is part of the solution but I have also explained that, because the conflict happens with functions, the changes are often disjoint. Person A found it reasonable to name their variables conn. At some point Person B found it reasonable to add a function named Plug.Test.conn/0. And now there is the ambiguity and a hard to debug issue due to the composition of two possibly good decisions. I believe that's why the issue became more apparent and more frequent as projects and the community grew.

Another good solution to this problem would be to rely less on imports. But better names and less imports, although good practices, do not effectively solve the issue.

You are correct in pointing out that the issue we are solving with this warning happens in rebinding. When you rebind a variable, you need to be careful of changing the meaning of later uses of that variable. My counter argument though is that I have a *variable* changing the meaning of a *variable* and since variables belong to the current scope only (and the scope rules got saner with time), it is simpler to grasp. That's different from a variable changing the meaning of a *function*. Which is harder to relate with.

The more I explain this, the clearer it becomes to me variables and functions have no business in stepping on each other's namespaces.

Dave Thomas

unread,
Dec 4, 2016, 8:49:39 PM12/4/16
to elixir-lang-core, da...@pragdave.me, jose....@plataformatec.com.br

The more I explain this, the clearer it becomes to me variables and functions have no business in stepping on each other's namespaces.

Except that classically a variable is simply a zero arity function with a shorthand creation syntax.

OK, one more try. If I say `Mod.a`, then 1.4 (correctly) treats it as a call—I don't need the ()s. A bare function call means the current module. So could we also support `.a` as a function call in the default module? (I know the answer to this. But I wanted to trigger some lateral thinking on your part. Some part of me is sure that this () thing is a move in the wrong direction, and I'd really like to find the magic button to help you see it too)

If you keep these warnings going forward, I don't think you should change iex—it should be consistent with the rest of the language. 


Paul Schoenfelder

unread,
Dec 4, 2016, 9:45:03 PM12/4/16
to elixir-l...@googlegroups.com, da...@pragdave.me, José Valim
I just want to throw in my two cents. I don't think a warning should be generated if there is no ambiguity about a name. I see warnings primarily as a means of saying "hey, you've done this thing, and it may not behave the way you expect it to, or may cause errors at runtime, you should change it to this other thing instead", not "hey, you've done this thing, and there's no problem with it now, but there could be, maybe". If warnings are not of the former type, then they should be warnings which can be disabled with a compiler flag, because it becomes a question of style vs actually warning about an unsafe condition.

Personally, if I have to append all zero-arity function calls with parens, I can deal with it, but I find it pretty annoying that it's something I have to go update all my code to address, just because it *might* cause a problem for me, or someone else, if a certain set of conditions are met in the future. If, on the other hand, a warning was generated *because I actually met those conditions*, it would be helpful, and would likely tell me to be more conscious and careful in order to prevent making the same mistake again in the future.

I find it a little difficult to understand why always warning is preferable to warning conditionally for this one.

Paul


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

José Valim

unread,
Dec 5, 2016, 3:53:51 AM12/5/16
to Paul Schoenfelder, elixir-l...@googlegroups.com, Dave Thomas

I just want to throw in my two cents. I don't think a warning should be generated if there is no ambiguity about a name.

Please re-read my replies and let me know if it is still not clear. The simple fact this feature exists, it means every time you are changing or reading code, you have an increased cognitive load because you are never sure what "hello" means. Even if there is no name clash in that particular case, since you can only know there is no name clash after careful examination.

It is exactly the same case with the imperative assignment that was deprecated. Maybe you never relied on it, but the fact it exists, means that everyone reading your code and every code you read, needs to be carefully read with the consideration of their existence.
 
I find it a little difficult to understand why always warning is preferable to warning conditionally for this one.

I have also explained in a previous reply how this will lead to more false positives and more undesirable situations if a conditional warning is implemented.


José Valim

unread,
Dec 5, 2016, 3:57:42 AM12/5/16
to Dave Thomas, elixir-lang-core
OK, one more try. If I say `Mod.a`, then 1.4 (correctly) treats it as a call—I don't need the ()s. A bare function call means the current module. So could we also support `.a` as a function call in the default module?

Sorry but I am not sure I see the logic here. Maybe you are trying to convince me from the syntax perspective while I am mostly concerned with the semantics? For vars and functions, different semantics and scopes, all the trouble came from using the same syntax.

Ólafur Arason

unread,
Dec 5, 2016, 5:57:41 AM12/5/16
to elixir-lang-core
I think that if you have introduced a var anywhere in your module that has the same name as a zero arity function then this warning should be shown that solves all the problems that have been stated.

We compile our code with warnings as errors so it's very important for us to have warnings that point out problems in our code.

I feel like the sentiment about being able to use zero arity function without parentheses is pretty split in this thread. If there was a consensus about this warning I would be fine with it.

Regards,
Olaf

José Valim

unread,
Dec 5, 2016, 7:06:29 AM12/5/16
to elixir-l...@googlegroups.com
I think that if you have introduced a var anywhere in your module that has the same name as a zero arity function then this warning should be shown that solves all the problems that have been stated.

I have explained in a previous reply why this is harmful and why it does not solve all the problems stated. The warning will not only show up when you introduce a variable, but also when you introduce a new function in the module and that will trigger warnings in unrelated part of the code.

While having the discussion is important, it is also important to move the discussion forward. This is the third or fourth time that "emit a warning only if there is a variable and a function" is proposed after it was already explained why such is considered harmful. This means either 1. the previous reply was not read, which is frustrating because it puts me in the position of repeating the same points over and over again, or 2. the previous reply was read but folks don't agree with its conclusions. If the latter, nobody is explaining *what they don't agree with*, which does not allow the conversation to move forward.

I will copy the reply on why warning only if there is a function is a bad idea one last time:

> So going back to the cases the current warning is meant to solve that I sent in an earlier email, warning only if it shadows a function, does not solve the first case although it does also solve the second case.

> However, it does not solve the contextual overhead, When changing or reading code, I still need to carefully look at the surrounding context to see if the variable is not being used in order to avoid the warning (or I can write the code and wait for the compiler to tell me).

> But to make things worse, the suggestion of only warning if there is a function, means that by simply adding a function into a given module, you may now get warnings on other functions in that same module, simply because you defined a new function 100LOC below. It is even worse if you consider imports: if you are importing or using an external library and it adds a new function to its API, you may have warnings when updating your dependency.

> Those are really undesired consequences. I believe we should either define that one of them has higher precedence (Elixir v1.3) or make sure they don't conflict (Elixir v1.4).

I would love to continue the discussion but if it ultimately ends up with me repeating previous points, it will eventually lose interest.


José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

José Valim

unread,
Dec 5, 2016, 7:16:33 AM12/5/16
to elixir-l...@googlegroups.com
To further clarify the previous response, we have three options:

1. Do not warn if a variable is used as a function call (Elixir v1.3)

2. Warn if a variable is used as a function call (Elixir v1.4)

3. Warn if there is a variable and a function with the same name (proposed in this thread)

The response above was about option 3. I consider it to be the worst option because it solves less than half of the problems the warning was meant to solve while having introducing drawbacks that do not exist in options 1 and 2.

So if you believe 3 is still the way to go, please *elaborate on the points on why it is a good reason* and why you agree or disagree with the drawbacks previously explained.

José Valim
Skype: jv.ptec
Founder and Director of R&D

José Valim

unread,
Dec 5, 2016, 7:28:05 AM12/5/16
to elixir-l...@googlegroups.com
Sorry for one extra amendment: I also want to add that, if my replies are not clear, I will gladly expand and give more examples. The point is that we need more feedback than "you should warn only when there is a function and a variable".




José Valim
Skype: jv.ptec
Founder and Director of R&D

Ólafur Arason

unread,
Dec 5, 2016, 9:25:23 AM12/5/16
to elixir-lang-core, jose....@plataformatec.com.br
I don't have horse in the race, I'm fine with either 2 or 3. But like with
def, defmodule, @attributes and other uses of non parenthesis function
calls, there are nice uses of non parenthesis in zero arity functions. But
like you have stated there are some really bad problem that can arise and
it makes the code unclear sometimes. I only proposed option 3 because it
would cover most of the problems with the dual use of names. But I also
do realize that it's harder to implement and might not be a perfect solution.

It's like reusing variable name, it's not necessary, it causes some problems but
it's sometimes better than:
green = green()

Regards,
Olaf
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

Chris McCord

unread,
Dec 5, 2016, 10:12:24 AM12/5/16
to elixir-l...@googlegroups.com
I used to be on Dave’s side of the argument, but after seeing people get bit by the ambiguity over and over, including myself, I’m definitely for this change. Even with a heightened awareness of the issue, I still find I will occasionally waste cycles on frustrating debugging to find it was a null arity function call where I thought there was a variable reference. `foo()` vs `foo` is slightly less beautiful in my eyes, but its alignment with explicit over implicit wins out.

Dave Thomas

unread,
Dec 5, 2016, 10:20:54 AM12/5/16
to elixir-lang-core

I bow to the collective (well, actually, to the people with write access)... :)

Andrea Leopardi

unread,
Dec 5, 2016, 10:26:12 AM12/5/16
to elixir-l...@googlegroups.com
Throwing my 2c in: I'm for emitting the warning all the way. I vastly prefer reducing the cognitive load to "pretty" code, and personally I consider the call with parens way prettier than the call without (as it's clear that it's a function call, and there is no ambiguity). May be not strictly related, but this is the same reason why I always use parens on 0-arity functions called on a module that is stored in a variable, such as my_module.my_fun(), in order to avoid the cognitive load of having to find out if my_module is a module or a map.

Andrea Leopardi

To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsub...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/F70227D9-DD4A-4962-9330-8F3DD3283B3E%40chrismccord.com.

Allen Madsen

unread,
Dec 5, 2016, 10:29:31 AM12/5/16
to elixir-l...@googlegroups.com, José Valim
I would also argue for case 3.

> However, it does not solve the contextual overhead, When changing or reading code, I still need to carefully look at the surrounding context to see if the variable is not being used in order to avoid the warning (or I can write the code and wait for the compiler to tell me).

I'm not sure why I would worry much about it if the compiler is going
to tell me about. The whole point of the compiler warning you is so
that you don't have to think about and still end up with something
correct.

> But to make things worse, the suggestion of only warning if there is a function, means that by simply adding a function into a given module, you may now get warnings on other functions in that same module, simply because you defined a new function 100LOC below. It is even worse if you consider imports: if you are importing or using an external library and it adds a new function to its API, you may have warnings when updating your dependency.

The reason this feels unconvincing to me is because the situations
where this comes up would be very infrequent for me. It's possible a
module I import will define a function with zero arity that shadows a
variable that I define. However, the chance of that seems very small.
A more likely, scenario is that I define a function with zero arity
within a module and also use the same name for a variable. For me,
that would also be particularly unlikely though. The case where I
define zero arity functions that don't have conflicting local
variables is very high though. So, allowing zero arity functions
without parens optimizes for my most common use case.

In the situation where there is a conflict, I need effective tools to
find the issue. An underlying assumption I had was that when the
warning was detected, it would tell me the locations of the conflict.
In the case of the 100LOC below conflict, the distance doesn't really
matter because the warning told me on line 1 I have a variable that
conflicts with a method defined on line 100. In the case of the method
coming from a module import, I would expect it to tell me what import
included the method that is causing the conflict.

Allen Madsen
http://www.allenmadsen.com
> https://groups.google.com/d/msgid/elixir-lang-core/a9fd5f51-f302-49ff-b228-04ff2ff248bd%40googlegroups.com.

Amos King

unread,
Dec 5, 2016, 11:09:37 AM12/5/16
to elixir-l...@googlegroups.com
Is this something that could be solved with an external tool added to your build chain?

Amos King
Binary Noggin
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK-y3CtFSjWZUeXp9djKhc97xykaObc6F39BE4PLQTQjQSaFuw%40mail.gmail.com.

J. Daniel Ashton

unread,
Dec 5, 2016, 11:34:51 AM12/5/16
to elixir-l...@googlegroups.com
Has anyone watched Avdi's episode on barewords?  https://www.rubytapas.com/2012/10/01/episode-004-barewords/  (I haven't checked whether this one is free or requires a subscription.)  The concept seems to apply to what Dave was requesting.


José Valim

unread,
Dec 5, 2016, 1:37:46 PM12/5/16
to Allen Madsen, elixir-l...@googlegroups.com
The reason this feels unconvincing to me is because the situations
where this comes up would be very infrequent for me.

That's a very good point I forgot to take into account in my initial reply. Thank you!

OvermindDL1

unread,
Dec 5, 2016, 2:10:00 PM12/5/16
to elixir-lang-core, allen.c...@gmail.com, jose....@plataformatec.com.br
I am very for not only this warnings but making it entirely in to an error.  Perhaps only defmacro's should be allowed to be called without parenthesis and all functions defined with `def` otherwise must have parenthesis.

Although I'd also be for going the other way, removing parenthesis (and commas separating arguments) except for blocks, but that is more functional than what Elixir is going for.  ^.^

Myron Marston

unread,
Dec 6, 2016, 2:19:12 AM12/6/16
to elixir-lang-core, allen.c...@gmail.com, jose....@plataformatec.com.br
Personally, I've been gravitating toward the explicitness of `fun_call()` over `fun_call`, but have been on the fence about the warning.  Something occurred to me while reading this conversation, though: I think this change means that any attempted shadowing in Elixir will yield a warning or error, which seems like a very good thing, because it means a name always refers unambiguously to one thing, and what that thing is can be known just be looking at the code.

For those of you who are opposed to this warning: are you concerned primarily about the affect it'll have on new code that you write or about the need to update existing code to not trigger the warning?  If it's more about the need to update existing code (as Steve Pallen mentioned), I think a tool to help upgrade to 1.4 would go along way towards addressing that concern.  I'm thinking of a static analysis tool that would update `foo` to `foo()` in any place that trigger the warning.  You could run it on a code base and deal with the warnings in about 30 seconds.

I imagine the core team doesn't have the time to make such a tool a priority, but if someone from the community wanted to step up and build a migration tool that made it easy to update code to avoid new Elixir warnings for things that can be addressed by an automated tool, it would be really, really useful.  We had something similar for RSpec 3 when that was released called transpec and it made a huge difference in helping people upgrade.

José, you mentioned that this warning was originally slated for 1.3 and then was pushed back to 1.4, which makes me wonder: do you have a roadmap for future warnings you plan to add to Elixir?  If there are things you plan to warn on in the future, it would be nice if they were documented so we can begin avoiding them now.

Myron

José Valim

unread,
Dec 6, 2016, 8:56:55 AM12/6/16
to elixir-l...@googlegroups.com
José, you mentioned that this warning was originally slated for 1.3 and then was pushed back to 1.4, which makes me wonder: do you have a roadmap for future warnings you plan to add to Elixir?  If there are things you plan to warn on in the future, it would be nice if they were documented so we can begin avoiding them now.

Those are always listed or in the issues tracker (if yet to be implemented) or in the CHANGELOG (if already implemented) and I couldn't find anything in there. So there is nothing planned.


jbo...@wistia.com

unread,
Dec 6, 2016, 1:48:58 PM12/6/16
to elixir-lang-core
Would it be possible to have a flag attribute to disable warnings on particular modules or something? Not the greatest solution, but it could potentially be enough to support DSLs and IEx helpers

On Friday, December 2, 2016 at 10:42:45 PM UTC-5, Dave Thomas wrote:

I’ve been loving 1.4 for a few weeks now, but I am bugged by the new warning:

 warning: variable "int" does not exist and is being expanded to "int()", please use parentheses to 
   remove the ambiguity or change the variable name

Partly it is because it makes my code a lot uglier. For example, in quixir, instead of

test "two plain types" do 
  ptest a: int, b: list do
   ​ assert is_integer(a) 
    assert is_list(b) 
  end 
end

I now have to write:

test "two plain types" do
  ptest a: int(), b: list() do
   . . .

Ugh. Even worse, the premise of the warning seems wrong. It assumes int is a variable, which doesn’t exist. But it does know that int is a function, because if I misspell it and put ()s on, I get a compilation error. So why can’t it just do that: is a bare name is encountered that isn’t a variable, just internally tack on the ()s are see what happens. Which I think is the old way.

Basically, what compelling problem drove this change? It isn’t an issue I ever had before, and the change seems to make my code worse.

Dave

Michał Muskała

unread,
Dec 6, 2016, 1:51:32 PM12/6/16
to elixir-l...@googlegroups.com

> On 6 Dec 2016, at 19:48, jbo...@wistia.com wrote:
>
> Would it be possible to have a flag attribute to disable warnings on particular modules or something? Not the greatest solution, but it could potentially be enough to support DSLs and IEx helpers
>

In DSLs you could manually upgrade the variable into a function call instead of relying on the compiler to do so (and emit a warning).

The AST of a variable is {name, meta, context}, while AST of a function call is {name, meta, args}, in case of 0-arity calls the last argument would always be an empty list.

Michał.

hart...@gmail.com

unread,
May 31, 2017, 2:46:36 PM5/31/17
to elixir-lang-core
First, sorry to re-open an old thread and thanks for all the awesome work done on Elixir. I humbly think I might have 1 or 2 original thoughts on the matter.

I am in the camp of either (1) getting back to 1.3 behavior or less ideally (3) narrow down to only the cases when we have a function and a variable with the same name.

The original issue seems to be poor variable and function naming coupled with some unexpected pollution from imports. I don't think it's an issue with the language itself. If you take the case of the `conn` and `Plug.Test.conn/0` collisions, renaming the function to `build_connection/0` seems more straightforward than requiring everyone to use `()` for zero parameter function calls. 

Furthermore, having namespaces of variable and function mixing together seems to be the original intent of the language. Same way Ruby allows to not care if an object come from some code or it's already there. Of course, José Valim would know better what was the original intentions, but bare with me! If we want a strict separation of these 2 namespaces with the end goal of reducing cognitive load, shouldn't we have a different way of naming variable like other languages are doing with for example `$variable`? I know Dave Thomas wasn't serious, but I even like the idea of adding a dot in front of every functions - `.function` - keeping variables untouched. It feels embracing even more the functional way. It seems too late to make this kind of changes though.

Finally, my main point will be to make the parallel with Python `3.0` changes. Adding brackets to every function calls in order to be more explicit. The scope was larger, and Python `3.0` added also some other controversial changes in the way strings were handled that weren't liked by everyone. But, the push to require brackets was the main point of discord and it resulted in a schism in the community. Even after almost a decade, Python 2.7 is still there and Python 3.5 still has trouble making his nest despite being now faster and more features rich. I think we all want Elixir to succeed. It means more people will be using it resulting in less bugs and more libraries. However, this kind of changes - that seems trivial and reasonable - can hurt a community a lot more than it seems.

However, at the end of the day, this is not a democracy, and we'll all respect whatever you will decide.

Thanks,
Julian

Michał Muskała

unread,
May 31, 2017, 5:36:33 PM5/31/17
to elixir-lang-core
To be honest, I don't think this is a big issue or a problem that needs changes. The biggest proof, it's not a problem in practice is the fact that it's almost 6 months since elixir 1.4 introduced this warning. After first couple weeks, when most libraries were upgraded to get rid of the warning, there aren't many complaints. As far as I can remember, it hasn't come up even once last couple months on either Elixir Slack or IRC.

Michał.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

Paul Schoenfelder

unread,
May 31, 2017, 7:35:59 PM5/31/17
to elixir-l...@googlegroups.com
I agree, it's an inherently subjective issue as well, which means no solution will make everyone happy. Additionally, at this point, the change has pretty well propagated throughout the community, so the "damage is done" so to speak (though in my opinion the current solution is the best given the language constraints). 

Understanding the Lisp 1 vs 2 debate is also critical here - fundamentally something has to give, you either need a way to syntactically determine whether a name belongs to a function or variable, and thus be able to have separate namespaces, or share a namespace and define rules which resolve the ambiguity with zero-arity functions. The latter is the best option in my opinion, and what led to the current solution. Doing nothing is the least desirable option here.

Paul


Hartator

unread,
Jun 1, 2017, 2:56:14 PM6/1/17
to elixir-l...@googlegroups.com
Understood. Thanks for the answers.

On Wed, May 31, 2017 at 6:35 PM, Paul Schoenfelder <paulscho...@gmail.com> wrote:
I agree, it's an inherently subjective issue as well, which means no solution will make everyone happy. Additionally, at this point, the change has pretty well propagated throughout the community, so the "damage is done" so to speak (though in my opinion the current solution is the best given the language constraints). 

Understanding the Lisp 1 vs 2 debate is also critical here - fundamentally something has to give, you either need a way to syntactically determine whether a name belongs to a function or variable, and thus be able to have separate namespaces, or share a namespace and define rules which resolve the ambiguity with zero-arity functions. The latter is the best option in my opinion, and what led to the current solution. Doing nothing is the least desirable option here.

Paul

On Wed, May 31, 2017 at 4:36 PM Michał Muskała <mic...@muskala.eu> wrote:
To be honest, I don't think this is a big issue or a problem that needs changes. The biggest proof, it's not a problem in practice is the fact that it's almost 6 months since elixir 1.4 introduced this warning. After first couple weeks, when most libraries were upgraded to get rid of the warning, there aren't many complaints. As far as I can remember, it hasn't come up even once last couple months on either Elixir Slack or IRC.

Michał.

On 31 May 2017, 20:46 +0200, hart...@gmail.com, wrote:
First, sorry to re-open an old thread and thanks for all the awesome work done on Elixir. I humbly think I might have 1 or 2 original thoughts on the matter.

I am in the camp of either (1) getting back to 1.3 behavior or less ideally (3) narrow down to only the cases when we have a function and a variable with the same name.

The original issue seems to be poor variable and function naming coupled with some unexpected pollution from imports. I don't think it's an issue with the language itself. If you take the case of the `conn` and `Plug.Test.conn/0` collisions, renaming the function to `build_connection/0` seems more straightforward than requiring everyone to use `()` for zero parameter function calls. 

Furthermore, having namespaces of variable and function mixing together seems to be the original intent of the language. Same way Ruby allows to not care if an object come from some code or it's already there. Of course, José Valim would know better what was the original intentions, but bare with me! If we want a strict separation of these 2 namespaces with the end goal of reducing cognitive load, shouldn't we have a different way of naming variable like other languages are doing with for example `$variable`? I know Dave Thomas wasn't serious, but I even like the idea of adding a dot in front of every functions - `.function` - keeping variables untouched. It feels embracing even more the functional way. It seems too late to make this kind of changes though.

Finally, my main point will be to make the parallel with Python `3.0` changes. Adding brackets to every function calls in order to be more explicit. The scope was larger, and Python `3.0` added also some other controversial changes in the way strings were handled that weren't liked by everyone. But, the push to require brackets was the main point of discord and it resulted in a schism in the community. Even after almost a decade, Python 2.7 is still there and Python 3.5 still has trouble making his nest despite being now faster and more features rich. I think we all want Elixir to succeed. It means more people will be using it resulting in less bugs and more libraries. However, this kind of changes - that seems trivial and reasonable - can hurt a community a lot more than it seems.

However, at the end of the day, this is not a democracy, and we'll all respect whatever you will decide.

Thanks,
Julian

On Tuesday, December 6, 2016 at 12:51:32 PM UTC-6, Michał Muskała wrote:

> On 6 Dec 2016, at 19:48, jbo...@wistia.com wrote:
>
> Would it be possible to have a flag attribute to disable warnings on particular modules or something? Not the greatest solution, but it could potentially be enough to support DSLs and IEx helpers
>

In DSLs you could manually upgrade the variable into a function call instead of relying on the compiler to do so (and emit a warning).

The AST of a variable is {name, meta, context}, while AST of a function call is {name, meta, args}, in case of 0-arity calls the last argument would always be an empty list.

Michał.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/Otz0uuML764/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK%3D%2B-TtgE4Y9mgbaShoUedM-psuJfMtk95jVDiFa2yFZwmxcsw%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages