在伞形应用程序中使用捕获运算符 - elixir

时间:2017-04-26 12:02:27

标签: elixir

我有一个伞应用程序,
在一个应用程序中(例如:App1)我将一个函数作为参数发送到位于另一个应用程序中的另一个函数(例如:App2)。 我发送这样的函数:

defmodule App1.Bar do
  def bar_bar(fun) do
    fun.()
  end
end

defmodule App2.Foo do
  def foo_foo do
    App1.Bar.bar_bar(&Io.puts(Project.get.project[:app]))
  end
end

我的问题是:
该函数是否实际在App1App2中执行(在哪里定义)?

3 个答案:

答案 0 :(得分:2)

如果您要执行此类操作,则会在App1执行,但App2中将评估。< / p>

让我们来看看这实际意味着什么。假设我们有以下模块:

defmodule Foo do
  def foo(fun) do
    IO.puts "I AM IN FOO"
    fun.()
  end
end
defmodule Bar do
  def bar(), do: Foo.foo(fn -> IO.inspect baz() end)
  defp baz(), do: "baz"
end

iex(1)> Bar.bar()
I AM IN FOO
"baz"
"baz"

这里要注意的两个重要部分是:

  • 在我们的匿名函数中看到打印之前,我们打印了I AM IN FOO。这意味着该功能在Foo
  • 中执行
  • 我们能够评估Bar.baz/0,即使Foo无法看到该功能(它也没有自己的baz/0功能)。这意味着在传递给Foo.foo/1之前必须对其进行评估。

答案 1 :(得分:1)

此示例将抛出CompileError,因为Capture运算符假定您将提供函数,如下所示:

&Mod.fun/arity
&local/arity

或者至少你将使用Capture运算符指定一个参数,例如。 &Mod.fun(&1)

在这种情况下,你的论点是错误的。 如果没有Capture操作符并使用常规方法制作匿名函数,您将拥有:

fn -> Io.puts(Project.get.project[:app]) end

因此它适用于Project.get.project[:app]的内容。我不熟悉那个花哨的模块,但它应该在你调用它的模块中工作。

答案 2 :(得分:1)

要控制项目执行功能,您可以使用Mix.Project.in_project/4

Mix.Project.in_project :my_app, "/path/to/my_app", fn module ->
  "Mixfile is: #{inspect module}"
end
#=> "Mixfile is: MyApp.Mixfile"

一般来说,在函数之间传递lambdas将保留定义lambda的词法范围。

但是,在进程之间传递lambda将导致self()计算为不同的值:

iex(10)> lambda = fn -> IO.puts("Lambda running in: #{inspect(self())}") end
#Function<20.52032458/0 in :erl_eval.expr/5>

iex(11)> lambda.()
Lambda running in: #PID<0.80.0>
:ok

iex(12)> spawn(lambda)
Lambda running in: #PID<0.96.0>
#PID<0.96.0>

这可以更改依赖于当前Process字典的任何内容的输出,例如Ecto.Repo在事务内部时将当前连接存储在Process字典中。