[RFC] New anonymous function invocation syntax

620 views
Skip to first unread message

José Valim

unread,
Nov 4, 2013, 4:42:35 PM11/4/13
to elixir-l...@googlegroups.com
Hello everyone,

This is a small discussion about removing the dreaded dot from the anonymous function invocation syntax. The current:

    iex> sum = fn a, b -> a + b end
    iex> sum.(1, 2)
    1 + 2

Will become:

    iex> sum = fn a, b -> a + b end
    iex> sum(1, 2)
    1 + 2

The consequence of this change is that anonymous functions can now shadow local function calls. If this is a good thing or not is being discussed literally for decades, as seen in the Lisp-1 vs Lisp-2 discussions. The change is mostly straight-forward except by one inconsistency that would be introduced into the language which I want to discuss.

Today, in order to invoke a local function, parentheses are not required:

    do_something 1, 2, 3

Parentheses are not required even if the function expects no arguments:

    do_something #=> translated as do_something() as long as there is no do_something var

And we want anonymous functions to behave the same:

    iex> sum = fn a, b -> a + b end
    iex> sum 1, 2

The inconsistency lies in the fact we can't invoke an anonymous functions that takes no arguments without parentheses (because that simply means accessing a variable):

    iex> print = fn -> IO.puts "hello" end
    iex> print
    #Function<...>
    iex> print()
    hello

That is very inconsistent. Since the whole point of removing the dot is to allow shadowing, invoking anonymous and local functions should be bound by the same syntax. That said, we have two options:

1. Give up on the new anonymous function syntax.

2. Deprecate (during a long, long period) invocation of local functions without parentheses. That said, if you have `foo` in your code calling a local function foo/0, a warning asking parentheses to be explicitly added will now be issued. In case you choose 2, should this rule also apply to remote calls on functions with no args? I.e. should `MyModule.foo` also emit the same warning or the warning should only apply to local functions?

So which option do you choose and why?

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

Yurii Rashkovskii

unread,
Nov 4, 2013, 5:57:32 PM11/4/13
to elixir-l...@googlegroups.com
I would rather suggest to (1) give up on the new anonymous function syntax, or go for an inconsistency rather than doing (2), i.e. deprecating braketless calls. Those are very common, convenient, used way more often than direct anonymous function invocation.


--
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.
For more options, visit https://groups.google.com/groups/opt_out.

Ngoc Dao

unread,
Nov 4, 2013, 6:06:44 PM11/4/13
to elixir-l...@googlegroups.com
In Scala, invoking anonymous function requires parentheses.

I suggest keeping the new anonymous function syntax, and requiring
that invoking anonymous function requires parentheses.

Christopher Keele

unread,
Nov 4, 2013, 6:27:26 PM11/4/13
to elixir-l...@googlegroups.com
> In Scala, invoking anonymous function requires parentheses.

I like this option. I think the cognitive dissonance of only anon functions requiring parens would be less than the extra period syntax or only requiring parents on parameterless functions. It allows you to shadow with conventional syntax, but also makes handling anon functions a little more explicit.

-- 
Christopher Keele
Sent with Sparrow

Dave Thomas

unread,
Nov 4, 2013, 6:35:00 PM11/4/13
to elixir-l...@googlegroups.com

I'd vote for (3): require () for parameterless calls only.

All the solutions have a little associated inconsistency. To me, this solution minimizes the impact.

The most common use of a function with no parameters will be to create something to pass to map and friends, and there you are -passing- the function, not calling it, so there's no impact.

Dave

--

TR NS

unread,
Nov 4, 2013, 7:16:22 PM11/4/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br


On Monday, November 4, 2013 4:42:35 PM UTC-5, José Valim wrote:

The inconsistency lies in the fact we can't invoke an anonymous functions that takes no arguments without parentheses (because that simply means accessing a variable):

    iex> print = fn -> IO.puts "hello" end
    iex> print
    #Function<...>
    iex> print()
    hello


If the goal is to have annonymous functions behave like local methods, why not make the exception that anonymous functions require a special syntax to access them as a variable. e.g.


    iex> print = fn -> IO.puts "hello" end
    iex> print
    hello
    iex> \print
    #Function<...>

Of course the trick then becomes finding a good syntax for that.



Devin Torres

unread,
Nov 4, 2013, 11:17:56 PM11/4/13
to elixir-l...@googlegroups.com, José Valim
Am I the only one who likes the current way to call an anonymous function and the clarity it provides?

The only reason it's confusing is because people are trying to mind map other language semantics onto Elixir.



José Valim

unread,
Nov 5, 2013, 2:05:26 AM11/5/13
to elixir-l...@googlegroups.com

I'd vote for (3): require () for parameterless calls only.


Dave, just to be clear: do you mean require () for parameters calls on both anonymous and local functions or just local ones? If the former, that is exactly what I (tried to) proposed with 2). :)

José Valim

unread,
Nov 5, 2013, 2:10:04 AM11/5/13
to Devin Torres, elixir-l...@googlegroups.com
Am I the only one who likes the current way to call an anonymous function and the clarity it provides?

The only reason it's confusing is because people are trying to mind map other language semantics onto Elixir.

As I said, people are having this discussion for decades, see the Lisp-1 vs Lisp-2 debacle. I don't think we will find an answer now, so let's please not go into this discussion.

I also want to point out that requiring parentheses for parameterless calls may potentially help us in the future, for example, if we make Date(2013, 10, 1) valid syntax, the parameterless version would also require explicit parentheses: Date(). So it would help make everything consistent.

José Valim

unread,
Nov 5, 2013, 2:11:24 AM11/5/13
to elixir-l...@googlegroups.com
In Scala, invoking anonymous function requires parentheses.

I suggest keeping the new anonymous function syntax, and requiring
that invoking anonymous function requires parentheses.

So what would happen in this case:

    def foo do
      bar = fn a, b -> a + b end
      bar a, b
    end

    def bar(a, b) do
      a - b
    end

Will "bar a, b" invoke the local function, the anonymous function, or error?

Alexei Sholik

unread,
Nov 5, 2013, 3:57:30 AM11/5/13
to elixir-lang-core
I'm with Dave on this one. Using () for all 0-arity function calls is better IMO. It makes the code clearer both for beginners (is `self` a variable or a function?) and veterans.

What's more interesting–and I'm not sure if Dave implied this or not–passing an arbitrary-arity function as an argument won't require the capture syntax. What I mean is this:

parent = self()   # this is now the only way to _call_ self
self_fn = self     # error? or perhaps this is equal to &self/0? I'm suggesting the latter

Likewise,

Enum.map col, one_arg_func
# or
Enum.map(col, one_arg_func)

If it's clear in the grammar that `one_arg_func` above is not a call (I think it's clear enough for humans), then it can be treated as if it was `&one_arg_func/1`).

Anonymous functions will work just the same way:

selfie = fn -> IO.puts "You are beautiful" end
selfie()  # call
selfie    # variable lookup

one_arg_func = fn x -> -x end
Enum.map col, one_arg_func


--
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.
For more options, visit https://groups.google.com/groups/opt_out.



--
Best regards
Alexei Sholik

José Valim

unread,
Nov 5, 2013, 4:15:22 AM11/5/13
to elixir-l...@googlegroups.com
I'm with Dave on this one. Using () for all 0-arity function calls is better IMO. It makes the code clearer both for beginners (is `self` a variable or a function?) and veterans.

This is exactly what I intended. The only question is, should remote functions with no parameters also require parentheses? I.e. should Kernel.self be allowed?
 
parent = self()   # this is now the only way to _call_ self
self_fn = self     # error? or perhaps this is equal to &self/0? I'm suggesting the latter

Likewise,

Enum.map col, one_arg_func
# or
Enum.map(col, one_arg_func)

If it's clear in the grammar that `one_arg_func` above is not a call (I think it's clear enough for humans), then it can be treated as if it was `&one_arg_func/1`).

We can't do this because functions are recognized by name and arity. So if you have foo/1, foo/2 and foo/3 and simply call "foo", how would we know which one of the three do you want? Our compiler cannot figure that out and even if it did, it is perfectly fine for a function to accept anonymous functions with 1, 2, 3 or more arguments.

 one_arg_func, without parentheses should mean a variable access, nothing less, nothing more.

Eric Meadows-Jönsson

unread,
Nov 5, 2013, 4:30:11 AM11/5/13
to elixir-l...@googlegroups.com
I agree with Dave and Alexei. Please keep 0-arity remote function invocation syntax the same. Having to write `file.mtime()` for record fields wouldn't be very nice.


--
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.
For more options, visit https://groups.google.com/groups/opt_out.



--
Eric Meadows-Jönsson

José Valim

unread,
Nov 5, 2013, 4:31:26 AM11/5/13
to elixir-l...@googlegroups.com
On Tue, Nov 5, 2013 at 10:30 AM, Eric Meadows-Jönsson <eric.meado...@gmail.com> wrote:
I agree with Dave and Alexei. Please keep 0-arity remote function invocation syntax the same. Having to write `file.mtime()` for record fields wouldn't be very nice.

Excellent point, thanks Eric!

Alexei Sholik

unread,
Nov 5, 2013, 4:52:47 AM11/5/13
to elixir-lang-core
We can't do this because functions are recognized by name and arity. So if you have foo/1, foo/2 and foo/3 and simply call "foo", how would we know which one of the three do you want? 

I was half-joking, but I don't think it's an entirely lost cause. For instance, Enum.map always expects a function of 1 argument. That could be encoded in the type spec and used by compiler. It doesn't matter what the arity of the given function is, because this is valid code in Elixir:

Enum.map col, &div/2
Enum.map col, &Nonexistent.func/1

So the only thing that matters is what Enum.map expects.

Our compiler cannot figure that out and even if it did, it is perfectly fine for a function to accept anonymous functions with 1, 2, 3 or more arguments.

I can't come up with an example of such a function. But I don't see as a problem anyway.

f = fn x, y, z -> ... end
some_func f                 # works like before, f is just a variable
some_func self            # error if there is no spec for some_func saying what arity it expects
some_func_expecting_arity_3 self  # error, because &self/3 would fail
some_func Kernel.self  # a call


--
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.
For more options, visit https://groups.google.com/groups/opt_out.



--
Best regards
Alexei Sholik

José Valim

unread,
Nov 5, 2013, 5:28:48 AM11/5/13
to elixir-l...@googlegroups.com


On Tuesday, November 5, 2013, Alexei Sholik wrote:
We can't do this because functions are recognized by name and arity. So if you have foo/1, foo/2 and foo/3 and simply call "foo", how would we know which one of the three do you want? 

I was half-joking, but I don't think it's an entirely lost cause. For instance, Enum.map always expects a function of 1 argument. That could be encoded in the type spec and used by compiler. It doesn't matter what the arity of the given function is, because this is valid code in Elixir:

Enum.map col, &div/2
Enum.map col, &Nonexistent.func/1

So the only thing that matters is what Enum.map expects.

Typespecs are not used, at least not today, for any code optimization. Such approach would make typeclasses at the worst case required to all functions that expect anonymous functions, otherwise the proposed syntax wouldn't work.

Even today dialyzer takes some time to run and build the Typespec info, something that would now be required for all projecs and definitely affect compile time. So yes, it can be done, but not without completely affecting how the language works.
 

Our compiler cannot figure that out and even if it did, it is perfectly fine for a function to accept anonymous functions with 1, 2, 3 or more arguments.

I can't come up with an example of such a function. But I don't see as a problem anyway.

Enum.map/2 did that before streams. And you can think of a function that passed no arguments to a callback and then starts passing a new one, but keeps the previous approach for backwards compatibility.

The issue with this approach is that such changes can break user code. Imagine the following case:

1) the user has foo/1 and foo/2 locally
2) Enum.map expects a function with 1-arity
3) Enum.map(..., foo) works, because it is not ambiguous and used foo/1
4) Enum.map changes to allow functions with 2-arity
5) user code breaks because it is now ambiguous

Even if we say the code does not break upfront (let's assume a silly default of picking the function with minor arity), we are adding ambiguity and it can still fail if foo/1 and foo/2 have different behaviour in the opposite scenario (when a function supports 2-arity and then starts supporting 1-arity).

We need name and arity to identify functions, to skip that is to ask for trouble. And I don't think all this trouble and ambiguity is worthy for saving 3 chars (even if you are half joking :).



 


--

TR NS

unread,
Nov 5, 2013, 6:33:47 AM11/5/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br

Wait. We already have a syntax for that.

    &print

So what is wrong with this approach? Why has no one commented on this?


Eric Meadows-Jönsson

unread,
Nov 5, 2013, 7:02:23 AM11/5/13
to elixir-l...@googlegroups.com

Wait. We already have a syntax for that.
    &print
So what is wrong with this approach? Why has no one commented on this?

That wouldn’t work. We need special syntax for invocation of anonymous functions because elixir is a dynamic language. Imagine you have the following expression: var. Should we generate code for function invocation or for variable access? We don't know because at compile time we don’t know if var is an anonymous function or any other type.

Peter Minten

unread,
Nov 5, 2013, 7:18:12 AM11/5/13
to elixir-l...@googlegroups.com
Just to add my 2c:

I like the removal of the dot.

With regard to forcing the parens on zero args functions I wonder if
it's possible to do something like Python.

In Python "list.reverse" returns "<method 'reverse' of 'list' objects>",
you need explicit parens to invoke it. There is an exception for things
called properties:

class Foo(object):
@property
def bar(self):
return 2

f = Foo()
f.bar # returns 2

There's some stuff going on under the hood with the @property decorator
and such but the main point is that Python has tricks to make both
forced-parens and no-parens work.

I could imagine that in Elixir we could do something like this:

defmodule Foo
@prop
def bar(), do: 2
end

Where the `def` macro when seeing `@prop` would add the function atom to
a module property and the compiler would look in that property to see if
the call should be allowed or denied.

An advantage would be that this info could be picked up by ex_doc as
well, it could be used to convey whether a nullary function is more of
the computing kind (requires parens) or more like a cheaply computed
field (no parens required).

The big disadvantage is obvious, it does complicate things.

On 11/05/2013 10:30 AM, Eric Meadows-J�nsson wrote:
> I agree with Dave and Alexei. Please keep 0-arity remote function
> invocation syntax the same. Having to write `file.mtime()` for record
> fields wouldn't be very nice.
>
>
> On Tue, Nov 5, 2013 at 10:15 AM, Jos� Valim <jose....@plataformatec.com.br

Alexei Sholik

unread,
Nov 5, 2013, 7:28:36 AM11/5/13
to elixir-lang-core
@pminten The compiler does not know what you're calling.

NonexistentModule.func is as valid as Kernel.self. The compiler doesn't know about record calls either:

a.self    # is `a` a record?

# not necessarily
a = :erlang
a.self    # returns #PID<0.40.0>

Keeping optional parentheses for remote calls seems a good tradeoff.

Kernel.self         # unambiguous, modules can't have variables
something.self   # unambiguous, even if `something` is a record, it's still a call to `self()` returning something



On 11/05/2013 10:30 AM, Eric Meadows-Jönsson wrote:
> I agree with Dave and Alexei. Please keep 0-arity remote function
> invocation syntax the same. Having to write `file.mtime()` for record
> fields wouldn't be very nice.
>
>
> On Tue, Nov 5, 2013 at 10:15 AM, José Valim <jose....@plataformatec.com.br

Eric Meadows-Jönsson

unread,
Nov 5, 2013, 7:27:55 AM11/5/13
to elixir-l...@googlegroups.com

Where the `def` macro when seeing `@prop` would add the function atom to
a module property and the compiler would look in that property to see if
the call should be allowed or denied.

Again, the problem here is that elixir is a dynamic language. We have the same issue with typespecs that someone talked about above. How can the compiler look for @prop when it doesnt know the module at compile time: mod = Foo; mod.bar?




On 11/05/2013 10:30 AM, Eric Meadows-Jönsson wrote:
> I agree with Dave and Alexei. Please keep 0-arity remote function
> invocation syntax the same. Having to write `file.mtime()` for record
> fields wouldn't be very nice.
>
>
> On Tue, Nov 5, 2013 at 10:15 AM, José Valim <jose....@plataformatec.com.br



--
Eric Meadows-Jönsson

Peter Minten

unread,
Nov 5, 2013, 7:38:57 AM11/5/13
to elixir-l...@googlegroups.com
Ah, was kinda afraid that something like that might be the case. Given the limited options I think opt-parens only for remote call is the best choice.

Alexei Sholik <alcos...@gmail.com> schreef:

--
Verzonden van mijn Android telefoon met K-9 Mail.

Saša Jurić

unread,
Nov 5, 2013, 8:22:35 AM11/5/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
In general, requiring parens for local calls seem fine. I do have a couple of questions though:

1. What will happen with imported modules?

import MyModule
...
fun_from_my_module  # imported from MyModule

In particular, this is important for autoimported functions from Kernel (e.g. self)


2. Continuing on 1, will it still be possible to call imported macros without parens?

3. I expect pipelines should work without parens, but I'd just like to verify:

var
|> fun
|> another_fun

Tom Janssens

unread,
Nov 5, 2013, 8:54:38 AM11/5/13
to elixir-l...@googlegroups.com
This might not be possible, but it would make everything consistent...

Op dinsdag 5 november 2013 13:02:23 UTC+1 schreef Eric Meadows-Jönsson:

José Valim

unread,
Nov 5, 2013, 9:31:58 AM11/5/13
to elixir-l...@googlegroups.com
Even if it was possible, I really dislike this approach because it is poorly extensible. For example, a simple property like name may be changed later to be the concatenation of first and last names. The cost of such computation is very likely irrelevant to the app but now I need to update all invocations to account the change. There is a lot of theory coming from Eiffel (uniform access principle iirc) detailing why this is not desired.
On 11/05/2013 10:30 AM, Eric Meadows-Jönsson wrote:
> I agree with Dave and Alexei. Please keep 0-arity remote function
> invocation syntax the same. Having to write `file.mtime()` for record
> fields wouldn't be very nice.
>
>
> On Tue, Nov 5, 2013 at 10:15 AM, José Valim <jose....@plataformatec.com.br


--

José Valim

unread,
Nov 5, 2013, 9:31:59 AM11/5/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br

1. What will happen with imported modules?

import MyModule
...
fun_from_my_module  # imported from MyModule

In particular, this is important for autoimported functions from Kernel (e.g. self)

Local, anonymous and imported functions with zero arity will require parentheses. 
 
3. I expect pipelines should work without parens, but I'd just like to verify:

var
|> fun
|> another_fun

Very good question. As there is no ambiguity in this case, I can see an argument for it not being required. But I can also see an argument for consistency as well.
 


--

Alexei Sholik

unread,
Nov 5, 2013, 9:38:45 AM11/5/13
to elixir-lang-core
Even if it was possible, I really dislike this approach because it is poorly extensible. For example, a simple property like name may be changed later to be the concatenation of first and last names. The cost of such computation is very likely irrelevant to the app but now I need to update all invocations to account the change. There is a lot of theory coming from Eiffel (uniform access principle iirc) detailing why this is not desired.

You may have misunderstood. Peter has mentioned that "There is an exception for things called properties". In Python, `a.b` may refer either to an instance variable `b` or to a method call that does anything at all. No previously written code is affected by changing `b` from an instance var to a property.
Best regards
Alexei Sholik

Sasa Juric

unread,
Nov 5, 2013, 10:03:30 AM11/5/13
to elixir-l...@googlegroups.com
On Nov 5, 2013, at 3:31 PM, José Valim wrote:

Local, anonymous and imported functions with zero arity will require parentheses. 

So calling any fun with arity > 0 will work without parens? In particular, it will not be necessary to use parens when calling macros (unless of course it accepts no argument)?



 
3. I expect pipelines should work without parens, but I'd just like to verify:

var
|> fun
|> another_fun

Very good question. As there is no ambiguity in this case, I can see an argument for it not being required. But I can also see an argument for consistency as well.
 

The complete consistency is not guaranteed in any case, since we can call external funs without parens, and we can call funs with arities > 0 without parens. In that sense, there are subtle syntax differences for the same thing (calling a fun). Which is why I start to wonder are the benefits of dropping current lambda call syntax really worth it.


In any case, having to write
  var
  |> fun()
  |> another_fun()
would in my opinion uglify the code a bit.

José Valim

unread,
Nov 5, 2013, 10:36:29 AM11/5/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br

1. What will happen with imported modules?

import MyModule
...
fun_from_my_module  # imported from MyModule

In particular, this is important for autoimported functions from Kernel (e.g. self)

Local, anonymous and imported functions with zero arity will require parentheses. 
 
3. I expect pipelines should work without parens, but I'd just like to verify:

var
|> fun
|> another_fun

Very good question. As there is no ambiguity in this case, I can see an argument for it not being required. But I can also see an argument for consistency as well.
 


--

José Valim

unread,
Nov 5, 2013, 10:36:28 AM11/5/13
to elixir-l...@googlegroups.com
Even if it was possible, I really dislike this approach because it is poorly extensible. For example, a simple property like name may be changed later to be the concatenation of first and last names. The cost of such computation is very likely irrelevant to the app but now I need to update all invocations to account the change. There is a lot of theory coming from Eiffel (uniform access principle iirc) detailing why this is not desired.

On Tuesday, November 5, 2013, Peter Minten wrote:
On 11/05/2013 10:30 AM, Eric Meadows-Jönsson wrote:
> I agree with Dave and Alexei. Please keep 0-arity remote function
> invocation syntax the same. Having to write `file.mtime()` for record
> fields wouldn't be very nice.
>
>
> On Tue, Nov 5, 2013 at 10:15 AM, José Valim <jose....@plataformatec.com.br

José Valim

unread,
Nov 5, 2013, 10:41:12 AM11/5/13
to elixir-l...@googlegroups.com
Ah, thanks Alexei (and sorry Peter)! I thought it worked as JavaScript (where the issue I described exists).
Best regards
Alexei Sholik

--
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.
For more options, visit https://groups.google.com/groups/opt_out.

Alexei Sholik

unread,
Nov 5, 2013, 10:52:27 AM11/5/13
to elixir-lang-core
Ah, thanks Alexei (and sorry Peter)! I thought it worked as JavaScript (where the issue I described exists).
 
It doesn't :) http://d.pr/i/1PIL
Best regards
Alexei Sholik

Yurii Rashkovskii

unread,
Nov 5, 2013, 11:12:29 AM11/5/13
to elixir-l...@googlegroups.com
Devin, I agree with you. 

I think ultimately fun.() is not bad, as this kind of anonymous function invocation is quite rare (anon functions are way more often used in Enum functions than directly) and this syntax actually discourages (or at least makes you pay extra attention) passing anonymous functions around directly as this is generally not a great practice for hot-upgrades as after two upgrades such functions will become badfuns. And this is a Good Thing.

Removing an ability to use argumentless local or imported functions without parens will just bring more noise into our code for very little benefit. It is a style question. Nobody prevents you from using () in your code *today*, but forcing everybody to do that seems like a suboptimal solution. I personally use imports and local functions a lot and I find the current state of things quite comfortable and balanced.


On Mon, Nov 4, 2013 at 8:17 PM, Devin Torres <de...@devintorr.es> wrote:
Am I the only one who likes the current way to call an anonymous function and the clarity it provides?

The only reason it's confusing is because people are trying to mind map other language semantics onto Elixir.

On Mon, Nov 4, 2013 at 6:16 PM, TR NS <tran...@gmail.com> wrote:


On Monday, November 4, 2013 4:42:35 PM UTC-5, José Valim wrote:

The inconsistency lies in the fact we can't invoke an anonymous functions that takes no arguments without parentheses (because that simply means accessing a variable):

    iex> print = fn -> IO.puts "hello" end
    iex> print
    #Function<...>
    iex> print()
    hello

If the goal is to have annonymous functions behave like local methods, why not make the exception that anonymous functions require a special syntax to access them as a variable. e.g.
    iex> print = fn -> IO.puts "hello" end
    iex> print
    hello
    iex> \print
    #Function<...>

Of course the trick then becomes finding a good syntax for that.


--
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.
For more options, visit https://groups.google.com/groups/opt_out.

José Valim

unread,
Nov 5, 2013, 11:14:28 AM11/5/13
to elixir-l...@googlegroups.com


Local, anonymous and imported functions with zero arity will require parentheses. 

So calling any fun with arity > 0 will work without parens? In particular, it will not be necessary to use parens when calling macros (unless of course it accepts no argument)?

Exactly. Basically, non-qualified calls with 0-arity require explicit parens. 
3. I expect pipelines should work without parens, but I'd just like to verify:

var
|> fun
|> another_fun

Very good question. As there is no ambiguity in this case, I can see an argument for it not being required. But I can also see an argument for consistency as well.
 

The complete consistency is not guaranteed in any case, since we can call external funs without parens, and we can call funs with arities > 0 without parens. In that sense, there are subtle syntax differences for the same thing (calling a fun). Which is why I start to wonder are the benefits of dropping current lambda call syntax really worth it.

Right, this is the discussion we need to have. 

In any case, having to write
  var
  |> fun()
  |> another_fun()
would in my opinion uglify the code a bit.

Right, the pipeline can be discussed separately because none of the functions have 0-arity.

Devin Torres

unread,
Nov 5, 2013, 2:55:57 PM11/5/13
to elixir-l...@googlegroups.com
Function invocation using no parens is a godsend for DSLs. Please don't change that.

My vote is for requiring parenthesis to call an anonymous function.

If I just wrote:

foo = fn -> :ok end
foo

Am I calling foo? No, I am likely trying to pass around the value foo, i.e. a function.


Yurii Rashkovskii

unread,
Nov 5, 2013, 10:42:03 PM11/5/13
to elixir-l...@googlegroups.com, de...@devintorr.es
I support not changing that.

Vassilis Rizopoulos

unread,
Nov 6, 2013, 4:26:05 AM11/6/13
to elixir-l...@googlegroups.com
Well, the DSL argument is what drives the compromise for me.
Removing parentheses and dots will just make DSLs more expressive and for that I would be willing to live with slight inconsistencies.
So my vote would be for required parentheses on /0 anonymous functions (for higher arity functions not using parentheses should be OK), removal of the dot and maintaining the ability to call (named) functions without parentheses.
Which I guess means I'm repeating what the majority say :P
V.-

José Valim

unread,
Nov 6, 2013, 7:27:50 AM11/6/13
to elixir-l...@googlegroups.com
Well, the DSL argument is what drives the compromise for me.
Removing parentheses and dots will just make DSLs more expressive and for that I would be willing to live with slight inconsistencies.
So my vote would be for required parentheses on /0 anonymous functions (for higher arity functions not using parentheses should be OK), removal of the dot and maintaining the ability to call (named) functions without parentheses.
Which I guess means I'm repeating what the majority say :P
V.-

In my opinion it needs to be one of:

1) keep as is
2) remove the dot and require parentheses for non-qualified 0-arity calls

Now there is a proposed third solution:

3) remove the dot but keep optional parentheses

The option 3) is very ambiguous and inconsistent. Here is why.

In 1), we know:

* foo.(...) is an anonymous function call
* foo(...) is a local/import call
* foo is ambiguous: or a var or a local call. This ambiguity has been fine so far because we don't have many functions with 0-arity and the ambiguity can be removed with explicit parentheses

In 2), we know:

* foo(...) is ambiguous: it is an anonymous function or a local/import call
* foo is a variable

In 3), however, we know:

* foo(...) is ambiguous: an anonymous function or a local/import call
* foo is ambiguous: is a variable or a local/import call, adding parentheses does not remove the ambiguity (simply shifts it)

In fact, for 3) it is easier to say we don't know anything because everything requires looking into the context and retrieving the appropriate information. And this fairly complicates matters. For example, macros now need to account the context when doing tree manipulations, our ASTs needs to hold more metadata information, etc.

What I am trying to say is that this discussion goes way beyond syntax. And while 3) seems ideal on the syntax, it is by far the worst solution on every other aspect. If someone is interested on the topic, I would recommend reading:


In particular the paper listed in the discussion:


Our current approach is a Lisp-2.

Chris McCord

unread,
Nov 6, 2013, 9:12:49 AM11/6/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
I agree with Devin and Yurri in that the current state of things is quite balanced and I find my own Elixir code uses anonymous functions much less often than imported and local module functions. Given this usage pattern, I actually prefer/don't mind the dot invocation since it explicitly shows what is going on, namely, it's invoking an anonymous or captured function. Optional parens for local functions is a must for me since it makes composing DSLs much nicer so I plead that we don't go the #2 route.

It appears that abandoning the removal of the dot invocation is actually the clearest approach and avoids introducing new inconsistencies, at the cost of a special case for anon funcs and newcomers learning the "dot syntax". For me, this is a pleasant tradeoff and swings my vote towards leaving the current state as is.


On Monday, November 4, 2013 4:42:35 PM UTC-5, José Valim wrote:
Hello everyone,

This is a small discussion about removing the dreaded dot from the anonymous function invocation syntax. The current:

    iex> sum = fn a, b -> a + b end
    iex> sum.(1, 2)
    1 + 2

Will become:

    iex> sum = fn a, b -> a + b end
    iex> sum(1, 2)
    1 + 2

The consequence of this change is that anonymous functions can now shadow local function calls. If this is a good thing or not is being discussed literally for decades, as seen in the Lisp-1 vs Lisp-2 discussions. The change is mostly straight-forward except by one inconsistency that would be introduced into the language which I want to discuss.

Today, in order to invoke a local function, parentheses are not required:

    do_something 1, 2, 3

Parentheses are not required even if the function expects no arguments:

    do_something #=> translated as do_something() as long as there is no do_something var

And we want anonymous functions to behave the same:

    iex> sum = fn a, b -> a + b end
    iex> sum 1, 2

The inconsistency lies in the fact we can't invoke an anonymous functions that takes no arguments without parentheses (because that simply means accessing a variable):

    iex> print = fn -> IO.puts "hello" end
    iex> print
    #Function<...>
    iex> print()
    hello

That is very inconsistent. Since the whole point of removing the dot is to allow shadowing, invoking anonymous and local functions should be bound by the same syntax. That said, we have two options:

1. Give up on the new anonymous function syntax.

2. Deprecate (during a long, long period) invocation of local functions without parentheses. That said, if you have `foo` in your code calling a local function foo/0, a warning asking parentheses to be explicitly added will now be issued. In case you choose 2, should this rule also apply to remote calls on functions with no args? I.e. should `MyModule.foo` also emit the same warning or the warning should only apply to local functions?

So which option do you choose and why?

Peter Minten

unread,
Nov 6, 2013, 9:20:11 AM11/6/13
to elixir-l...@googlegroups.com
There has been a lot of agreement on that f.() is better than requiring
local nullary functions to take parentheses. Allow me to be contrarian.
Even with the consequences for DSL's I consider the benefits of removing
the ugly weird use of the dot to outweigh the nullary function
inconsistency.

But then, it would hardly impact me as I tend to use parentheses for all
functions anyway.

On 11/06/2013 03:12 PM, Chris McCord wrote:
> I agree with Devin and Yurri in that the current state of things is quite
> balanced and I find my own Elixir code uses anonymous functions much less
> often than imported and local module functions. Given this usage pattern, I
> actually prefer/don't mind the dot invocation since it explicitly shows
> what is going on, namely, it's invoking an anonymous or captured function.
> Optional parens for local functions is a *must* for me since it makes
>> *Jos� Valim*

José Valim

unread,
Nov 6, 2013, 9:24:22 AM11/6/13
to elixir-l...@googlegroups.com
On Wed, Nov 6, 2013 at 3:12 PM, Chris McCord <ch...@chrismccord.com> wrote:
I agree with Devin and Yurri in that the current state of things is quite balanced and I find my own Elixir code uses anonymous functions much less often than imported and local module functions. Given this usage pattern, I actually prefer/don't mind the dot invocation since it explicitly shows what is going on, namely, it's invoking an anonymous or captured function. Optional parens for local functions is a must for me since it makes composing DSLs much nicer so I plead that we don't go the #2 route.

It appears that abandoning the removal of the dot invocation is actually the clearest approach and avoids introducing new inconsistencies, at the cost of a special case for anon funcs and newcomers learning the "dot syntax". For me, this is a pleasant tradeoff and swings my vote towards leaving the current state as is.

Thanks Chris. Just to be clear, parentheses will only be required for functions with zero-arity, which do not occur that frequently. Knowing that, do you still have the same opinion?

I am particularly fine with both approaches as we have precedence in other programming languages for both scenarios. For example, in Common Lisp, you need to use (FUNCALL SUM 1 2) to call an anonymous function and not simply (SUM 1 2). The expression f.(1, 2) in Elixir can be considered our own variation of FUNCALL (and arguably a less verbose one).

José Valim

unread,
Nov 6, 2013, 9:47:50 AM11/6/13
to elixir-l...@googlegroups.com
Btw, here is a script that you can pass an ebin and it will return all functions with zero-arity:


I ran it for Elixir and I got "138 out of 1774" functions with zero-arity for lib/elixir/ebin, where 30% of those are "Record.new" like functions, which are unlikely to be imported. Running it for projects like Dynamo and Ecto resulted in even smaller ratios, 24 out of 613 and 24 out of 686, respectively.




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


Sasa Juric

unread,
Nov 6, 2013, 9:51:52 AM11/6/13
to elixir-l...@googlegroups.com
If I understand correctly, this doesn't include local private functions?

--
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/_kEBXO0NRDY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.

José Valim

unread,
Nov 6, 2013, 9:55:27 AM11/6/13
to elixir-l...@googlegroups.com
You are right, Sasa, I have updated the gist. Here are the new results:

Elixir: Found 143 out of 2932
Dynamo: Found 29 out of 1090
Ecto: Found 26 out of 1131



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


--
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.

Yurii Rashkovskii

unread,
Nov 6, 2013, 10:49:47 AM11/6/13
to elixir-l...@googlegroups.com
I would still argue to keep things as is. The dot-invocation is still, by large, a rare thing. For example, in our project (~8K LOC), it is only used 8 times, while local argumentless invocation - 649 times. Makes you think.

But if that's not enough, the removal would introduce at least two inconsistencies:

1. Local argumentless calls have to have parens, but remote ones don't. There is a good *internal* explanation for this (removal of ambiguity) but this is hardly a naturally expectable consistent syntax. Just think about it — if you just started using Elixir and didn't know the whole story, wouldn't it be super confusing to learn that parens are optional...expect when they are not?.. Exactly, it would.
2. Piping local functions of arity of one will be even more confusing. Do they have to have mandatory parens? Yes? No? Why?


Sasa Juric

unread,
Nov 6, 2013, 11:05:03 AM11/6/13
to elixir-l...@googlegroups.com
The more I think about it, the more I'm inclined to side with Yurii.

Personally, the dot invocation for me always looked a bit clumsy. However, all of the solutions proposed here seems to be equally inconsistent. Probably the cleanest way would be to enforce parens everywhere, but that would kill the dsl-ish nature of the code.

Christopher Keele

unread,
Nov 6, 2013, 11:08:31 AM11/6/13
to elixir-l...@googlegroups.com
The more I think about it, the more I'm inclined to side with Yurii.
I believe I’m coming around to this perspective as well.

1) keep as is
2) remove the dot and require parentheses for non-qualified 0-arity calls
3) remove the dot but keep optional parentheses
2) seems more confusing and a DSL killer; 3)

Devin Torres

unread,
Nov 6, 2013, 11:39:00 AM11/6/13
to elixir-l...@googlegroups.com
Long live Lisp-2! Long live separate namespaces!

I understood f.() a lot more once I read José's Lego proposal, https://github.com/josevalim/lego-lang#parenthesis-applicability

In this regard, the "dot" is more of an operator to apply an anonymous function.


--

TR NS

unread,
Nov 6, 2013, 12:13:52 PM11/6/13
to elixir-l...@googlegroups.com


On Tuesday, November 5, 2013 7:02:23 AM UTC-5, Eric Meadows-Jönsson wrote:

Wait. We already have a syntax for that.
    &print
So what is wrong with this approach? Why has no one commented on this?

That wouldn’t work. We need special syntax for invocation of anonymous functions because elixir is a dynamic language. Imagine you have the following expression: var. Should we generate code for function invocation or for variable access? We don't know because at compile time we don’t know if var is an anonymous function or any other type.


Hmm... How does the compiler know the difference if `var` is a 0-arity method or a variable?


Gustavo Brunoro

unread,
Nov 6, 2013, 12:38:55 PM11/6/13
to elixir-l...@googlegroups.com
> Hmm... How does the compiler know the difference if `var` is a 0-arity method or a variable?
If `var` isn't on the binding, it should be a method. Try defining a variable named `self` (:


2013/11/6 TR NS <tran...@gmail.com>

Vincent Siliakus

unread,
Nov 6, 2013, 2:47:45 PM11/6/13
to elixir-l...@googlegroups.com
Op woensdag 6 november 2013 17:08:31 UTC+1 schreef Chris Keele:
The more I think about it, the more I'm inclined to side with Yurii.
I believe I’m coming around to this perspective as well.

I agree with Yurii's arguments too

Vincent Siliakus

unread,
Nov 6, 2013, 2:50:41 PM11/6/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
I guess a script that counts anonymous function invocations could provide a nice comparison  :)



Op woensdag 6 november 2013 15:55:27 UTC+1 schreef José Valim:

Damien Ragoucy

unread,
Nov 12, 2013, 7:28:07 PM11/12/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
Hello,

I just recently discovered Elixir.

I do a lot of Javascript and the thing I like is the very clear distinction between executed function and function as a value. A function can be part of an object instance or is simply an anonymous function freshly created, they react the same way and can be passed as values the same way.

Another very good point is the Uniform Access Principle, that I recently met and whose idea I roughly captured (attributes and calculated attributes are accessed the same way).

So the two points are:
1 - consistency
2 - UAC

A consequence of those two requirements is that it's impossible to have 0-arity anonymous functions to be passed "as is". Some people suggested to put a distinctive sign at the beginning of the function. Since with nested modules functions appear only at the end:

    ModuleFoo.ModuleBar.func # `func` is actually called

why not simply use the "/" sign at the end to mean "function as a value" without the need of a leading "&":

    ModuleFoo.ModuleBar.func/

When there is no ambiguity, simply use the slash sign ("/"): foo/. It might mean foo/0 in priority. If there is ambiguity, use foo/1, foo/2, foo/3...


There could be another syntax that would fit nicely with partially applied functions: the "_" sign as a placeholder. This way any function "called" with "_" would return a partially applied function.

So `foo _` would be the same than `foo(_)`, and the same than `foo/1`

Also something I didn't get is why do we have to call functions in piping operations:

    double = fn x -> x * 2 end
    1 |> double.()
    # returns 2

I feel it more like a succession of 1-arity functions as values (surely a Javascript deformation). Something like:

    1
    |> double _
    |> square _
    # result is 4

Also the "_" notation could be mixed with the &1, &2 ... &n notation.
For example:

                        # bar is a 3-arity function
    foo = bar &1, _, &2 # foo is a 1-arity function that returns a 2-arity function
    baz = foo 6         # baz is a 2-arity function
    qux = bar _, 6, _   # qux would be the same function than baz

If we consider piping as a succession of 1-arity functions as values, we could
do things like this:

    2
    |> times   _, 2
    |> times   3, _
    |> divides _, 4
    |> modulo  _, 2
    # result is 1

    1
    |> add_two/1
    |> divides &1, _
    # returns a 1-arity function that divides by 3

José Valim

unread,
Nov 13, 2013, 3:18:00 AM11/13/13
to elixir-l...@googlegroups.com
Damien,

Many of your proposals were already discussed in the mailing list, some extensively, so I would recommend you to do some digging to understand why it doesn't work as you proposed. I will only provide brief answers.

why not simply use the "/" sign at the end to mean "function as a value" without the need of a leading "&":

    ModuleFoo.ModuleBar.func/

When there is no ambiguity, simply use the slash sign ("/"): foo/. It might mean foo/0 in priority. If there is ambiguity, use foo/1, foo/2, foo/3...

In this thread I explained why automatically guessing the arity is error prone since whenever the function being called changes (for example, adds a new arity), the function you refer to may no longer be the same.

 
There could be another syntax that would fit nicely with partially applied functions: the "_" sign as a placeholder. This way any function "called" with "_" would return a partially applied function.

So `foo _` would be the same than `foo(_)`, and the same than `foo/1

We have been there. In fact, partial application did not require a leading & and that caused a lot of ambiguity. I do recommend you to check the archives.

If we consider piping as a succession of 1-arity functions as values, we could
do things like this:

    2
    |> times   _, 2
    |> times   3, _
    |> divides _, 4
    |> modulo  _, 2
    # result is 1

    1
    |> add_two/1
    |> divides &1, _
    # returns a 1-arity function that divides by 3

Extending the pipe has also been discussed many times as well as similar approaches as the ones proposed. The issue with all of them is that they end up making the pipe more complex and hard to follow. We have chosen to keep it simple.
Reply all
Reply to author
Forward
0 new messages