Elixirの構造体(struct)からモジュール名を取得する方法をまとめます。
簡単そうに見えてしっかり勉強しないとややこしい部分があります。
論よりRUN
早速IExを開きます。
iex
Time.utc_now/1を実行すると現在の時刻がTime
構造体で帰ってきます。
%Time{} = Time.utc_now()
構造体からモジュール名を取得する方法
構造体からモジュール名を取得する方法はいくつかあります。
- 隠された
:__struct__
キーの値を取得する - パターンマッチ
詳しくは公式ドキュメントのKernel.SpecialForms.%struct{}ページで解説されています。
:__struct__
キーの値と取得
time = Time.utc_now()
time |> is_struct()
time.__struct__
ここで注意が必要なのはMapとは異なり、Accessが実装されていないのでdata[key]
シンタックスは使えません。
iex> Time.utc_now()[:__struct__]
** (UndefinedFunctionError) function Time.fetch/2 is undefined (Time does not implement the Access behaviour. If you are using get_in/put_in/update_in, you can specify the field to be accessed using Access.key!/1)
(elixir 1.14.2) Time.fetch(~T[23:52:17.032092], :__struct__)
(elixir 1.14.2) lib/access.ex:288: Access.get/3
iex:33: (file)
:__struct__
キーに対してパターンマッチ
%{__struct__: mod} = Time.utc_now()
mod
構造体名に対してパターンマッチ
%mod{} = Time.utc_now()
mod
この特性は(構造体の名前が不要でも)ある値が構造体であることを確認する目的に利用できます。
%_{} = Time.utc_now()
struct
という名の関数たち
余談ですが、struct
という名の関数がいくつか存在します。知らないと混乱するかもしれません。
__struct__/1
- Kernel.defstruct/1により定義される
- 構造体そのものを返す関数
詳しくは公式ドキュメントのKernel.defstruct/1ページで解説されています。
Time.__struct__(
calendar: Calendar.ISO,
hour: 0,
microsecond: {0, 0},
minute: 0,
second: 0
)
結果は普通に構造体を生成する場合と同じです。
%Time{
calendar: Calendar.ISO,
hour: 0,
microsecond: {0, 0},
minute: 0,
second: 0
}
こういうヘンテコなコードも成り立ってしまいます。
Time.utc_now.__struct__.__struct__(
calendar: Calendar.ISO,
hour: 0,
microsecond: {0, 0},
minute: 0,
second: 0
)
Kernel.struct!/2
- 構造体を作成、更新する関数
struct!(Time, %{
calendar: Calendar.ISO,
hour: 0,
microsecond: {0, 0},
minute: 0,
second: 0
})
time = Time.utc_now
struct!(time, %{ microsecond: {0, 0} })
プロトコル
通常、構造体のモジュール名を取得することはあまりないと思います。:__struct__
キーはElixir言語にサポートされているので、使いたければ使って問題ないそうです。公式ドキュメントのKernel.SpecialForms.%struct{}のページで解説されています。
頻繁に構造体のモジュール名が必要となる場合、データと振る舞いがパッケージ化されたオブジェクト指向プログラミング的な構造になっている可能性があり、その場合はプロトコルを実装するとスッキリする場合あると以前聞いたことがあります。知らんけど。
ご参考までに