Muhammad Lukman Low
Muhammad Lukman Low

Reputation: 8533

Why does Elixir allow closures with undefined variables?

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

Upvotes: 5

Views: 1027

Answers (2)

Jos&#233; Valim
Jos&#233; Valim

Reputation: 51369

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.

Upvotes: 10

tkowal
tkowal

Reputation: 9289

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.

Upvotes: 3

Related Questions