Elixir 如何重构替换参数模式匹配的函数
我有这个功能:Elixir 如何重构替换参数模式匹配的函数,elixir,Elixir,我有这个功能: def update(%Evento{} = evento, attrs, dataSchema) do evento |> dataSchema.changeset(attrs) |> Repo.update() end 它与%Evento{}结构绑定。 我想让它独立于结构,而是传递一个参数,这样在调用函数时,我可以传递%Evento{},%News{},%Contact{}或任何我想要的结构,同时保持相同的功能/模式匹配检查。您可以使用模
def update(%Evento{} = evento, attrs, dataSchema) do
evento
|> dataSchema.changeset(attrs)
|> Repo.update()
end
它与%Evento{}
结构绑定。我想让它独立于结构,而是传递一个参数,这样在调用函数时,我可以传递
%Evento{}
,%News{}
,%Contact{}
或任何我想要的结构,同时保持相同的功能/模式匹配检查。您可以使用模式%\u{}
接受任何结构作为参数:
def update(%_{} = struct, attrs, dataSchema) do
...
end
或者,您可以使用模式%module{}
和保护接受一组白名单结构:
def update(%module{} = struct, attrs, dataSchema) when module in [Evento, Foo, Bar] do
...
end
编辑:更新为使用新的
%module{}
模式,如Łukasz Niemier在评论中建议的那样 您可以使用模式%\u{}
接受任何结构作为参数:
def update(%_{} = struct, attrs, dataSchema) do
...
end
或者,您可以使用模式%module{}
和保护接受一组白名单结构:
def update(%module{} = struct, attrs, dataSchema) when module in [Evento, Foo, Bar] do
...
end
编辑:更新为使用新的
%module{}
模式,如Łukasz Niemier在评论中建议的那样 虽然@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
我们在这里要做的是:我们使用\uuuuu(opts)宏声明调用\uu的参数中指定的尽可能多的子句。为了简单起见,此示例不允许传递公共块
(它是硬编码的),但也可以很容易地修改接受不同公共块的代码
让我们测试一下:
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
后者将成功调用Test.Test/0
中第一行和第二行的update
,第三行调用失败:
%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
希望这能有所帮助。虽然@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
我们在这里要做的是:我们使用\uuuuu(opts)
宏声明调用\uu的参数中指定的尽可能多的子句。为了简单起见,此示例不允许传递公共块
(它是硬编码的),但也可以很容易地修改接受不同公共块的代码
让我们测试一下:
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
后者将成功调用Test.Test/0
中第一行和第二行的update
,第三行调用失败:
%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
希望这会有帮助。为什么不从模式中删除%Evento{}=
?如果您只想接受结构,那么def update(%{{uuuuuu struct\uuuu:}=struct,attrs,dataSchema)执行。还是我误解了什么?@Dogbert你说得对。我只想接受structs。你能把这变成一个答案,这样我就可以接受了吗?为什么不从模式中删除%Evento{}=
?如果您只想接受结构,那么def update(%{{uuuuuu struct\uuuu:}=struct,attrs,dataSchema)执行。还是我误解了什么?@Dogbert你说得对。我只想接受structs。你能把这变成一个答案,这样我就可以接受了吗?我个人认为%module{}
语法比%{{code>结构:module}
@ukaszNiemier谢谢,我忘了最近在长生不老药中添加了这些!我已经更新了答案。我个人认为%module{}
语法比%{{uuuuuu struct\uuuuuuu:module}
@ukaszNiemier谢谢,我忘了最近在Elixir中添加了这些语法!我已经更新了答案。