Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Elixir allow closures with undefined variables?

Tags:

elixir

I can understand this:

iex(7)> outside_val = 5
5
iex(8)> print = fn() -> IO.puts(outside_val) end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(9)> print.()  
5
:ok

What I don't get so much is why is it Elixir allows the print function to be defined even if outside_val is not defined and only error out later? There is no way to pass in 'outside_val' after the closure has been defined anyway, so isn't it better for Elixir to check for existence of variable during creation ?

What I mean is this:

iex(2)> print = fn () -> IO.puts(outside_val) end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(3)> outside_val = 5
5
iex(4)> print.()
** (RuntimeError) undefined function: outside_val/0
like image 554
Muhammad Lukman Low Avatar asked Feb 09 '15 06:02

Muhammad Lukman Low


2 Answers

This is a bug in Elixir which will be fixed in v1.1 (already in the master branch):

Interactive Elixir (1.1.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> print = fn () -> IO.puts(outside_val) end
** (CompileError) iex:1: undefined function outside_val/0

The current implementation delays the expansion to invoke the function in IEx.Helpers. In master, we simply import IEx.Helpers, so we no longer need to expand outside_val later.

like image 191
José Valim Avatar answered Nov 18 '22 03:11

José Valim


There is couple of steps, when defining a function in Erlang (and in Elixir, since it is built on top of ErlangVM).

Firstly, you tokenize the input:

{ok, Ts, _} = erl_scan:string("fun() -> Z + 1 end.").

Then, you create the abstract syntax tree:

{ok, [ListAST]} = erl_parse:parse_exprs(Ts).

Last step is to evaluate it with:

Bindings = [{'Z', 1}].
erl_eval:expr(ListAST, Bindings).

In the last step, Erlang can see, that there are undefined variables and raise an exception.

In Elixir most language features are implemented as macros, so the last step is not taken during function definition, but when it is called. I am not sure, if you are able to check, if all variables are bound inside macro definition. If it is possible - that would be cool solution.

like image 36
tkowal Avatar answered Nov 18 '22 03:11

tkowal