LoginSignup
8
0

More than 1 year has passed since last update.

ElixirのAccess behaviourを完全に理解した

Last updated at Posted at 2021-12-24

TIL

  • Access behaviourは、KeywordMapに対応できるが、structには対応できない。
  • ElixirのstructsがAccess behaviourを実装しないのにはわけがある。
  • 事前にアトムでキーが定義されたMapやstructは、ドット記法(例、data.key)でのアクセスが推奨されている。

Access behaviour

The Access module defines a behaviour for dynamically accessing keys of any type in a data structure via the data[key] syntax.

Accessモジュールは、data[key]構文を介してデータ構造内の任意のタイプのキーに動的にアクセスするための動作を定義します。

(翻訳 by Google)

KeywordMapは似てはいますが、全く異なるものです。Accessモジュールの提供するdata[key]構文は、KeywordMapの両方に対応していて便利です。
Good and Bad Elixir by Chris Keathleyでは、後々のリファクタリングの容易性を考慮して、Map.get/2Keyword.get/2よりAccessを使用したようが良いのではと提唱しています。

Accessをつかってみる

KeywordMapの差異を気にせずに値を取得できます。便利!

k = [abc: [xyz: 123]]
m = %{abc: %{xyz: 123}}

Keyword.get(k, :abc)
Map.get(m, :abc)

k[:abc][:xyz]
m[:abc][:xyz]

k[:abc][:bad][:xyz] # nil
m[:abc][:bad][:xyz] # nil

get_in(k, [:abc, :xyz])
get_in(m, [:abc, :xyz])

しかしながらstructは別です。structに対してAccessを使用することはできません。

defmodule MyStruct do
  defstruct [:abc]
end

s = %MyStruct{abc: %{xyz: 123}}

s[:abc]
** (UndefinedFunctionError) function MyStruct.fetch/2 is undefined (MyStruct does not implement the Access behaviour)
    MyStruct.fetch(%MyStruct{abc: %{xyz: 123}}, :abc)
    (elixir 1.13.0-rc.0) lib/access.ex:285: Access.get/3

get_in(s, [:abc])
** (UndefinedFunctionError) function MyStruct.fetch/2 is undefined (MyStruct does not implement the Access behaviour)
    MyStruct.fetch(%MyStruct{abc: %{xyz: 123}}, :abc)
    (elixir 1.13.0-rc.0) lib/access.ex:285: Access.get/3

# Kernel.get_in/2だけは裏技があります。
get_in(s, [Access.key(:abc), Access.key(:xyz)])

なぜElixirのstructがAccess behaviourを実装しないのか

Accessのドキュメントを見てみると、どうも意図的にそうしているようです。

... since structs are maps and structs have predefined keys, they only allow the struct.key syntax and they do not allow the struct[key] access syntax.

... 構造体はマップであり、構造体には事前定義されたキーがあるため、構造体はstruct.key構文のみを許可し、struct[key]アクセス構文は許可しません。

(翻訳 by Google)

よく考えてみたら、納得できました。structを定義する時点でキーを明示しているので、動的でキーの存在を気にしないAccess behaviourよりキーの存在を保証してくれるstruct.key構文の方が良い気がします。

どうしてもstructにAccess behaviourを実装したい場合

個人的には、Elixirコアチームの考えに共鳴するので、structはstruct.key構文のみでアクセスするようにしようと考えています。

万一どうしてもstructにAccess behaviourを実装したい場合、自分で実装してもよいのですが、便利なElixirパッケージがあるので、車輪の再発明をする必要はありません。

iex

Mix.install([:accessible, :struct_access])

# Using accessible
defmodule MyAccesibleStruct1 do
  defstruct [:abc]
  use Accessible
end

# Using struct_access
defmodule MyAccesibleStruct2 do
  defstruct [:abc]
  use StructAccess
end

# Problem solved!!!
accesible = %MyAccesibleStruct1{abc: %{xyz: 123}}
accesible[:abc][:xyz]
get_in(accesible, [:abc, :xyz])

:tada::tada::tada:

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0