代码之家  ›  专栏  ›  技术社区  ›  Muhammad Lukman Low Samson

为什么Elixir允许带有未定义变量的闭包?

  •  5
  • Muhammad Lukman Low Samson  · 技术社区  · 10 年前

    我可以理解这一点:

    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
    

    我不太明白的是,为什么Elixir允许定义打印函数,即使没有定义outside_val,以后只会出错?无论如何,在定义了闭包之后,无法传递“outside_val”,所以Elixir在创建过程中检查变量的存在不是更好吗?

    我的意思是:

    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
    
    2 回复  |  直到 10 年前
        1
  •  10
  •   José Valim    10 年前

    这是Elixir中的一个bug,将在v1.1中修复(已在主分支中):

    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
    

    当前实现延迟了调用函数的扩展 IEx.Helpers 。在master中,我们只需导入 IEx.帮助者 ,因此我们不再需要扩展 outside_val 后来

        2
  •  3
  •   tkowal    10 年前

    在Erlang(以及Elixir,因为它是在ErlangVM之上构建的)中定义函数时,需要两个步骤。

    首先,将输入标记化:

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

    然后,创建抽象语法树:

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

    最后一步是评估它:

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

    在最后一步中,Erlang可以看到存在未定义的变量并引发异常。

    在Elixir中,大多数语言功能都是以宏的形式实现的,所以最后一步不是在函数定义过程中进行的,而是在调用时进行的。我不确定,如果您能够检查,是否所有变量都绑定在宏定义内。如果可能的话,这将是一个很酷的解决方案。