带有 do-block 的关键字参数
Keyword arguments with do-block
我有一个看起来像这样的函数。
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
块吗?
如果您愿意使用宏而不是函数,这可能会对您有所帮助:
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
虽然@bla 提供的答案在技术上是正确的(例如 macro
有效),但它几乎没有说明是什么和为什么。
首先,没有什么能阻止您将此语法与函数而不是宏一起使用,您只需要明确地将关键字参数与 do:
部分和其他任何内容分开:
defmodule Test do
# ⇓⇓⇓⇓⇓⇓⇓⇓⇓ HERE
def test(opts \ [], do: block) do
IO.inspect(block)
end
end
Test.test foo: 1 do
"Hello"
end
#⇒ "Hello"
你不能用函数实现的是生成一个可执行文件块。它将是静态的,如上例所示,因为函数是运行时公民。函数执行时的代码将已经编译,这意味着不能将 代码 传递给此块。也就是说,块内容将在 caller 上下文中执行,在 函数本身之前:
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.
我有一个看起来像这样的函数。
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
块吗?
如果您愿意使用宏而不是函数,这可能会对您有所帮助:
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
虽然@bla 提供的答案在技术上是正确的(例如 macro
有效),但它几乎没有说明是什么和为什么。
首先,没有什么能阻止您将此语法与函数而不是宏一起使用,您只需要明确地将关键字参数与 do:
部分和其他任何内容分开:
defmodule Test do
# ⇓⇓⇓⇓⇓⇓⇓⇓⇓ HERE
def test(opts \ [], do: block) do
IO.inspect(block)
end
end
Test.test foo: 1 do
"Hello"
end
#⇒ "Hello"
你不能用函数实现的是生成一个可执行文件块。它将是静态的,如上例所示,因为函数是运行时公民。函数执行时的代码将已经编译,这意味着不能将 代码 传递给此块。也就是说,块内容将在 caller 上下文中执行,在 函数本身之前:
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.