我有这个功能:
def update(%Evento{} = evento, attrs, dataSchema) do
evento
|> dataSchema.changeset(attrs)
|> Repo.update()
end
与%Evento{}
结构相关联。
我想使它独立于struct并传递一个参数,以便在调用函数时我可以传递%Evento{}
,%News{}
,%Contact{}
或我想要的任何结构,同时保持相同的功能/模式匹配检查。
答案 0 :(得分:6)
您可以使用模式%_{}
接受任何结构作为参数:
def update(%_{} = struct, attrs, dataSchema) do
...
end
或者,您可以使用模式%module{}
和警卫接受列入白名单的结构集:
def update(%module{} = struct, attrs, dataSchema) when module in [Evento, Foo, Bar] do
...
end
编辑:已更新为使用ŁukaszNiemier在评论中建议的新%module{}
模式!
答案 1 :(得分:1)
虽然@Dogbert的答案是[像往常一样]完美和自我解释,但我会在这里提出一些更麻烦的方法,允许回调不同类型的输入模块,仍然是100%干:
defmodule DryStructMatch do
defmacrop clause!(name, mod, fun) do
quote bind_quoted: [name: name, mod: mod, fun: fun] do
ast = {:%, [], [{:__aliases__, [alias: false], [mod]}, {:%{}, [], []}]}
quote do
def unquote(name)(unquote(ast) = struct, _arg1, _arg2) do
result = struct # |> ... COMMON BLOCK
unquote(:"#{name}_callback")(unquote(fun), result)
end
end
end
end
@doc ~S"""
Usage:
use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]
The above will be expanded into two `update` clauses, the former having
no callback, and the latter having a callback that spits the result
out to console before returning it (due to `&IO.inspect/1`.)
"""
defmacro __using__(opts) do
Enum.flat_map(opts, fn {name, mods} ->
[
quote do
defp unquote(:"#{name}_callback")(fun, result)
when is_function(fun, 1), do: fun.(result)
defp unquote(:"#{name}_callback")(_, result), do: result
def unquote(name)(struct, _arg1 \\ nil, _arg2 \\ nil)
end |
Enum.map(mods, fn
{mod, fun} -> clause!(name, mod, fun)
mod -> clause!(name, mod, nil)
end)
]
end)
end
end
我们在这里做的是:我们在__using__(opts)
宏调用的参数中声明了多个子句。为简单起见,此示例不允许传递公共block
(它是硬编码的),但也很容易修改接受不同公共块的代码。
让我们测试一下:
defmodule Foo, do: defstruct foo: 42
defmodule Bar, do: defstruct bar: 42
defmodule Baz, do: defstruct baz: 42
defmodule A do
use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]
end
defmodule Test do
def test do
IO.inspect A.update(%Foo{}) # prints %Foo{foo: 42} (explicit)
A.update(%Bar{}) # prints %Bar{bar: 42} (callback)
A.update(%Baz{}) # raises `FunctionClauseError`
end
end
Test.test
后者将成功为update
中的第一行和第二行调用Test.test/0
,而第三行则失败:
%Foo{foo: 42}
%Bar{bar: 42}
** (FunctionClauseError) no function clause matching in A.update/3
The following arguments were given to A.update/3:
# 1
%Baz{baz: 42}
# 2
nil
# 3
nil
iex:17: A.update/3
希望这可能有所帮助。