Elixir GUIDES を普通に読む7

Elixir GUIDES を読んだメモ

elixir-lang.jp

14. モジュールの属性

アノテーションとしてのモジュール

defmodule MyServer do
  @vsn 2
end
  • @vsn はモジュールのバージョンを示す属性
  • ここでは2と設定している

  • Elixirにはいくつかの予約済み属性がある

    • @moduledoc: モジュールのドキュメント
    • @doc: 関数やマクロのドキュメント
    • @behaviour: OTPまたはユーザー定義の動作の指定
    • @before_compile: モジュールがコンパイルされる前に呼び出されるフック
  • Math モジュールにドキュメントを追加してみる

defmodule Math do
  @moduledoc """
  Provides math-related functions.

  ## Examples

      iex> Math.sum(1, 2)
      3

  """

  @doc """
  Calculates the sum of two numbers.
  """
  def sum(a, b), do: a + b
end
  • ドキュメントを見てみる
iex(1)> h Math

                                      Math                                      

Provides math-related functions.

## Examples

    iex> Math.sum(1, 2)
    3

iex(2)> h Math.sum

                                 def sum(a, b)                                  

Calculates the sum of two numbers.

定数として

  • 値を見やすく、再利用したい場合にモジュール属性を使う
  • 値はコンパイル時にのみ存在する
defmodule MyServer do
  @initial_state %{host: "127.0.0.1", port: 3456}
  IO.inspect @initial_state
end

temporary storage として

  • Elixir の Web サーバー構築などに用いる共通基盤plug がある
  • Plugライブラリを用いることで、独自のPlugを定義できる
defmodule MyPlug do
  use Plug.Builder

  plug :set_header
  plug :send_ok

  def set_header(conn, _opts) do
    put_resp_header(conn, "x-header", "set")
  end

  def send_ok(conn, _opts) do
    send_resp(conn, 200, "ok")
  end
end

IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
Plug.Adapters.Cowboy.http MyPlug, []
  • plug/1 マクロを用いて、WEB要求がある時に呼び出される関数を接続した
  • plug/1 を呼び出すたびに、指定された引数を@plugs属性に保存する
  • call/2 で、@plugsの中のすべてのPlugを順番に実行する

15.構造体

構造体の定義

  • 構造体の定義は、defstructを使う
  • フィールドの定義と、デフォルト値を与えている
  • 構造体の名前はモジュール名となる
defmodule User do
  defstruct name: "John", age: 27
end

構造体へのアクセスと更新

iex(2)> %User{}
%User{age: 27, name: "John"}
iex(3)> john = %User{}
%User{age: 27, name: "John"}
iex(4)> john.name
"John"
iex(5)> jane = %{john | name: "Jane"}
%User{age: 27, name: "Jane"}
iex(6)> %{jane | oops: :field}
** (KeyError) key :oops not found in: %User{age: 27, name: "Jane"}
    (stdlib) :maps.update(:oops, :field, %User{age: 27, name: "Jane"})
    (stdlib) erl_eval.erl:256: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1263: :lists.foldl/3
  • |)は更新構文, 新しいキーが構造体に追加されることはない

  • 構造体はマップ

  • __struct__ で構造体の名前が取れる
iex(12)> is_map(john)
true
iex(13)> john.__struct__
User
  • ただ、構造体はマップに実装されるプロトコルはいずれも使用できない
    • 構造体を列挙したりアクセスしたりすることはできない

qiita.com

iex(14)> john[:name]
** (UndefinedFunctionError) function User.fetch/2 is undefined (User does not implement the Access behaviour)
    User.fetch(%User{age: 27, name: "John"}, :name)
    (elixir) lib/access.ex:267: Access.get/3
  • Mapモジュールの関数を用いることはできる
iex(14)> jane = Map.put(%User{}, :name, "Jane")
%User{age: 27, name: "Jane"}
iex(15)> Map.merge(jane, %User{name: "John"})
%User{age: 27, name: "John"}
iex(16)> Map.keys(jane)
[:__struct__, :age, :name]