作者:断肠人blogma_791 | 来源:互联网 | 2022-12-10 07:01
我有一个看起来像这样的功能.
def test(options \\ []) do
# Fun stuff happens here :)
end
它接受几个(可选的)关键字参数,包括do:
.我希望能够这样称呼它.
test foo: 1 do
"Hello"
end
但是,这会产生错误.
** (UndefinedFunctionError) function Example.test/2 is undefined or private. Did you mean one of:
* test/0
* test/1
Example.test([foo: 1], [do: "Hello"])
(elixir) lib/code.ex:376: Code.require_file/2
从错误中可以看出,上面的语法是两个单独的关键字列表.现在,我可以使用以下稍微不方便的语法来调用此函数
Example.test foo: 1, do: (
"Hello"
)
但有没有办法do
在一个函数调用中提供除了其他关键字参数之外的-block?
1> Aleksei Mati..:
虽然@bla提供的答案在技术上是正确的(例如macro
作品),但它几乎没有说明什么是什么.
首先,没有什么能阻止您使用函数而不是宏来使用此语法,您只需要将关键字参数显式地分隔为do:
part和其他任何内容:
defmodule Test do
# ????????? HERE
def test(opts \\ [], do: block) do
IO.inspect(block)
end
end
Test.test foo: 1 do
"Hello"
end
#? "Hello"
你有什么不能用的功能实现,是制作一个可执行块.它将是静态的,如上例所示,因为函数是运行时公民.函数执行时的代码将被编译,这意味着无法将代码传递给该块.也就是说,在函数本身之前,将使用调用者上下文执行块内容:
defmodule Test do
def test(opts \\ [], do: block) do
IO.puts "In test"
end
end
Test.test foo: 1 do
IO.puts "In do block"
end
#? In do block
# In test
通常情况下,您不期望Elixir块如何工作.那就是当宏来到现场时:宏是编译时的公民.在block
传递到do:
宏观的说法,将被注入的AST成Test.test/1
do
块,使
defmodule Test do
defmacro test(opts \\ [], do: block) do
quote do
IO.puts "In test"
unquote(block)
end
end
end
defmodule TestOfTest do
require Test
def test_of_test do
Test.test foo: 1 do
IO.puts "In do block"
end
end
end
TestOfTest.test_of_test
#? In test
# In do block
旁注:在评论中你说"我毫不犹豫地把它变成一个宏."这是完全错误的.函数和宏不可互换(虽然看起来像它们一样),但它们是完全不同的东西.宏应该用作最后的手段.宏注入AST就地.功能是AST.
2> bla..:
如果您愿意使用宏而不是函数,这可能对您有所帮助:
defmodule Example do
defmacro test(args, do: block) do
quote do
IO.inspect(unquote(args))
unquote(block)
end
end
end
样品用法:
iex(2)> defmodule Caller do
...(2)> require Example
...(2)>
...(2)> def foo do
...(2)> Example.test foo: 1 do
...(2)> IO.puts "Inside block"
...(2)> end
...(2)> end
...(2)> end
{:module, Caller,
<<70, 79, 82, 49, 0, 0, 4, 108, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 147,
0, 0, 0, 16, 13, 69, 108, 105, 120, 105, 114, 46, 67, 97, 108, 108, 101, 114,
8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>, {:foo, 0}}
iex(3)> Caller.foo
[foo: 1]
Inside block
:ok