正如代码https://github.com/elixir-plug/plug/blob/v1.5.0/lib/plug/builder.ex#L183所示,插件定义将在宏扩展阶段编译为AST。但为什么?为什么不保持插件定义并使用Enum.reduce_while
或递归来逐个调用插件?
答案 0 :(得分:1)
我能想到的两个原因:
性能。考虑这两个片段做同样的事情,但一个使用编译的函数调用,另一个使用Enum.reduce
和apply
:
defmodule A do
def add1(x), do: x + 1
def sub1(x), do: x - 1
def compiled(x) do
x
|> add1()
|> sub1()
|> add1()
|> sub1()
|> add1()
|> sub1()
|> add1()
|> sub1()
end
@pipeline [
{A, :add1},
{A, :sub1},
{A, :add1},
{A, :sub1},
{A, :add1},
{A, :sub1},
{A, :add1},
{A, :sub1}
]
def runtime(x) do
Enum.reduce(@pipeline, x, fn {module, function}, acc ->
apply(module, function, [acc])
end)
end
end
一个简单的基准测试显示运行时实现速度慢了5倍。
IO.inspect(
:timer.tc(fn ->
for _ <- 1..1_000_000, do: A.compiled(123)
:ok
end)
|> elem(0)
)
IO.inspect(
:timer.tc(fn ->
for _ <- 1..1_000_000, do: A.runtime(123)
:ok
end)
|> elem(0)
)
输出:
82800
433198
在编译时捕获错误。如果将模块传递给没有实现plug
的{{1}},则在编译时会出现错误,而不是您在运行时执行所有操作时通常会遇到的运行时错误。