如何检查Map是否也是一个Struct?

She*_*yar 10 elixir

在Elixir中,我可以通过调用来检查变量是a map还是a struct,Kernel.is_map/1因为Structs是下面的Maps,但是我想区分这两者.我知道我可以调用__struct__Struct来获取它的模块名称,但是在普通地图上调用它会抛出:

** (KeyError) key :__struct__ not found in: %{}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,如何检查变量是地图还是结构?


用例示例:

# I want to handle struct and map inputs differently in my Module

defmodule DifferentThings do
  def do_something(arg) when is_map(arg) do
    # Do something with Maps
  end

  def do_something(arg) when is_struct(arg) do
    # But handle Structs differently
    # Issue is, `is_struct` does not exist
  end
end
Run Code Online (Sandbox Code Playgroud)

Ale*_*nts 24

通常检查map是否是结构:

Map.has_key?(struct, :__struct__)
Run Code Online (Sandbox Code Playgroud)

对于不同的方法声明(更一般的方法是第二):

defmodule DifferentThings do
  def do_something(%{__struct__: _} = arg) do
    # ...
  end

  def do_something(arg) when is_map(arg) do
    # ...
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 如果需要模式匹配,可以使用`%{__ struct __:_} = struct` (3认同)

Raj*_*esh 8

可以在struct和map之间进行模式匹配,如下所示

defmodule DifferentThings do
  def do_something(arg = %_x{}) do
    IO.puts "This is a struct"
  end

  def do_something(arg = %{}) do
    IO.puts "This is a map"
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 非常喜欢`arg = %_x{}`,但你也可以做`arg = %_{}`。 (3认同)

Fre*_*Dog 5

您不能为 Map 与 Struct 使用保护设置单独的函数头,但您可以使用模式匹配来实现。

defmodule Guard do

  def foo(%{:__struct__ => x })  do
    Struct
  end

  def foo(x) when is_map x do
    Map
  end

end
Run Code Online (Sandbox Code Playgroud)


She*_*yar 4

检查某个术语是否为 a 的最简单方法map是使用:

Map.has_key?(map, :__struct__)
Run Code Online (Sandbox Code Playgroud)

它在守卫中不起作用,因此我们必须事先诉诸于模式匹配:__struct__函数子句中的键:

def some_fun(%__struct__: _module}), do: ...
Run Code Online (Sandbox Code Playgroud)

但这看起来很脏。


警卫:is_struct/1

从 Erlang/OTP 21 开始,引入了一些新的守卫,我们可以用它们来定义我们自己的守卫:

defguard is_struct(term) when is_map(term) and :erlang.is_map_key(:__struct__, term)
defguard is_struct(term, module) when is_struct(term) and :erlang.map_get(:__struct__, term) == module 
Run Code Online (Sandbox Code Playgroud)

现在您可以将它们用作函数防护:

def do_something(account) when is_struct(account, User) do
  # Do something with %User{} structs
end

def do_something(struct) when is_struct(struct) do
  # Do something with random structs
end

def do_something(map) when is_map(map) do
  # Do something with maps
end
Run Code Online (Sandbox Code Playgroud)

注意:这些很快就会添加到 Elixir 标准库本身中。请关注此ElixirForum 主题中的讨论。