Today, Elixir will transform var into var() in case var does not exist in the user context. I would like to propose those cases to emit a warning from today on.
The downsides is that we add a tiny inconsistency. We can make function call without parens unless it is a function with null-arity. On the other hand, we already have inconsistency in the sense we can only shadow function calls with null-arity by variables. So we are exchanging one by the other.
I have bitten by this issue twice today and I believe a warning to be a far compromise.
Strongly :+1:.
:+1:
+1 from me.
I agree with @lpil and dislike removing opportunity to call zero-arity functions without parentheses.
+1 from me as well
I also agree with @lpil and @romul. Not only would my codebase require dozens if not hundreds of edits to suppress the warnings, but the superfluous paren would make me sad.
It will push users to improve the quality of the code, as it will be less ambiguous, but I think it will affect the aesthetics of it.
For a historical reference, how it (variable is used as function call) became a problem one more time:
https://botbot.me/freenode/elixir-lang/2015-10-19/?msg=52227800&page=5
\cc @eddwardo
Small example:
defmodule Foo do
def bar do
IO.puts "started bar" # IT WILL HANG HERE
receive do
:msg -> IO.puts "received msg"
end
end
def start do
ping_pid = spawn(__MODULE__, bar, []) # forgot to pass :bar
ping_pid
end
end
I've got a little collection of thoughts on this subject:
Would this mean you'd have to use parens in pipelines as well? I really like the way pipelines work without parens on 0-arity functions, for example:
my_var
|> do_something
|> do_something_else
|> do_a_third_thing(1, 2)
To suppress this warning, it looks like you'd have to rewrite the above as:
my_var
|> do_something()
|> do_something_else()
|> do_a_third_thing(1, 2)
In this case, the first example makes it obvious that values are being piped to functions (you can't pipe to anything else), and looks a little sweeter in my opinion.
Another thing to think about is the way a human can read Elixir code.
def n do
10
end
def random_number do
:random.uniform(10)
end
Obviously this is a contrived example, but one can think through this code as _"I am defining n as 10"_, and _"I am defining random_number as a random number between 1 and 10"_. In these cases it makes sense to be able to retrieve those defined values simply with n and random_number. They _feel_ like values.
However, in the case of something like ExUnit.start/0, when you don't really care about the return value, only the side effects, it makes more sense to call it with parens (ExUnit.start()). That reads more as something like this _"I just performed the action of starting ExUnit"_.
Lastly, if all functions with 0-arity should be called with parens, wouldn't it make more sense for the preferred style of defining a function to be like this:
def start() do
:ok
end
start()
This would be more consistent than having a definition with one style and a call with another.
Anyway, my main point is that semantically, different styles make more sense in different scenarios. I'm not really sure what the answer is, as I definitely understand what @josevalim is saying (it's thrown me off before too), but those are my two cents :smile:
Would this mean you'd have to use parens in pipelines as well?
No, because are macros and the module compiler works on the AST after the macro is expanded. None of the code samples you have shown would need to change.
Sorry, closed by mistake.
Interesting, thanks for the explanation.
Still +1 to the original issue. I have adopted the following style for function calls long ago:
```
ExUnit.start
list
|> Stream.map(& &1 * &1)
|> Enum.reverse
```
this_returns_a_value()
|> Enum.filter(...)
|> some_local_function()
|> other_local_function(arg)
|> IO.iodata_to_binary
I suggest everyone else do the same. Even if this issue doesn't make it into Elixir 1.x, it will surely be part of Elixir 2.0. So you have a chance to save yourself some work in the future by adopting a good habit today :)
:+1: for @alco is saying. That goes along the lines of what I was trying to get across in the second half of my long comment above.
Does this also apply to macros?
test("""
Long test desc here
""') do
# ...
end
test """
Long test desc here
""" do
# ...
end
@lpil I don't think so, because the proposed warning would only be given for 0-arity functions.
Oh, right, of course. :)
+1 for a warning
+1 for warning, fyi: Credo already has a warning for this
I see how this could help, but I also prefer keeping parens off zero arty calls. I wonder if instead of warning when parens are missing, the warning would come up when there is a local variable name and a function with zero arity in the same scope.
Example:
conn = conn
conn = get(conn, ...etc.)
In this case the call to conn() is ambiguous and a bit confusing. I don't think the solution is to add parens though, the solution would be to rename the function to build_conn or something else. So I think that adding parens just masks the problem without really fixing the confusion.
That may not prevent all the bugs, but might at least prevent some of the more confusing situations?
I am strongly in favour of this warning because it makes Elixir code so much easier to read. Imagine you have this code:
def foo() do
# 50 lines of code
my_function(something)
# ...
end
Is something a variable or a private/imported 0-arity function? You can't know and you'll have to look through the 50 lines above the call to my_function/1 to find out. And bear in mind, it's not just a matter of finding something = ..., because you may have deeply nested pattern matches for example where something gets bound. With the current warning, we'll have to write my_function(something()), which will destroy every ambiguity.
That said, @paulcsmith Elixir's issue tracker is reserved for bugs so maybe we can move this discussion to the Elixir mailing list or to elixirforum? 馃槂
@whatyouhide Sure! I thought I saw a discussion label on this issue so I posted here. It seems most people are strongly in favor of adding the parens though so it probably isn't worth. Just wanted to voice an opinion in case it resonated with anyone :)
In this case the call to conn() is ambiguous and a bit confusing. I don't
think the solution is to add parens though, the solution would be to rename
the function to build_conn or something else. So I think that adding parens
just masks the problem without really fixing the confusion.
The call to conn() is never ambiguous. Only "conn". :)
@josevalim Yeah that's a good point! Thanks for responding
Imagine you have this code:
def foo() do
# 50 lines of code
my_function(something)
# ...
end
I'd prefer to have a warning if there is a function contains more than 50 lines )))
@romul if you have a comment related to this feature in particular, please add so, otherwise if you want to discuss other improvements and warnings, you should try the elixir-lang-core mailing list. Let's stay on topic please.
Most helpful comment
I'd prefer to have a warning if there is a function contains more than 50 lines )))