When using an anonymous function you write functionName.(a,b)
and get the value returned. I have seen other languages do functions like functionName(a,b)
without a dot. Is there a technical reasoning behind requiring a dot or is it just a syntax preference of Elixir’s creators? I just got really curious. Thanks
I think it’s better to say how they work internally…
A direct call blah()
cannot pass in hidden data very well.
An indirect call blah.()
can pass in hidden data very well.
I.E., indirect calls can be closures, direct calls cannot. The BEAM itself differentiates how they are called so without the .
it’d have to test every time if blah
was not known in scope.
On a tangent. Anonymous functions belong to the module that create it - so using an anonymous function loads that module (the closure has to belong somewhere).
Interesting.
I also assumed that it was partially a side effect of optional parentheses in Elixir. With optional parentheses, a function foo
with no parameters can be invoked by either foo()
, or foo
. If we wanted to work with the reference to the function foo
, how would we do that? We can’t pass around foo
, because that’s an execution of the function, not a reference to it.
That’s how I justify the special syntax in my mind.
This answer makes the most sense to me. Thank you
The main reason is that Elixir is a lisp-2 language - this means that variables and functions “occupy” different name spaces, so calling a function in the functions namespace and calling a function in the variables namespace has to work differently. Most languages are lisp-1 (which means that variables and functions are in the same namespace). Some other lisp-2 languages are Ruby, Common Lisp and Perl.
Being lisp-2 means that this code is valid:
def foo(arg), do: arg
def bar() do
foo = 1
foo(foo)
end
I.e. we can define a variable with the same name as local function and there is no shadowing occurring. This doesn’t work in lisp-1 languages, for example in Python:
>>> def foo(arg):
... return arg
>>> foo(1)
1
>>> foo = 1
>>> foo(foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
It also doesn’t matter if the language is statically typed, for example in C:
int foo(int a) {
return a;
}
int main() {
int foo = 1;
return foo(foo);
}
Fails to compile with:
test.c:7:15: error: called object type 'int' is not a function or function pointer
return foo(foo);
~~~^
1 error generated.
Neither choice is better or worse, it’s a design decision like many when constructing a language - each with it’s own trade-offs. It also has little to do with optional parenthesis. While yes, it’s true that optional parenthesis make it harder, this is not the main reason. This choice was dictated primarily by semantics of the language and not syntax.
From a practical view variables and function names have basically the same syntax so you need to differentiate between when you want the function name and when you want the value of the variable. So foo(bar)
means call the function called foo
while foo.(bar)
means call the value of the variable foo
.
If they have the same syntax then you must be able to differentiate which you mean. As @michalmuskala pointed out that with other languages where the is no difference in syntax you need to be able to differentiate, for example in CL (foo bar)
calls the function called foo
while (funcall foo bar)
calls the value of the variable foo
. You could view the .
as a short form of funcall
.