7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Elixirのチートシートを作ろう #15 モジュールその1

Last updated at Posted at 2024-12-20

1. OverView

いやー、タイトルだけ決めて書いたつもりになってますね。

本日は、Elixir Schoolのモジュールの解説、その1となっております。
https://elixirschool.com/ja/lessons/basics/modules

このモジュール、話は簡単なのですが…簡単なのですが、「何に使うか」の部分が理解しにくい。
延々と説明することになりますが、ぶっちゃけ、「後で読む」べきものなのかなと思ってます。

なので、古い言語の、しかも仕事の愚痴を含めた僕のお気持ちポエムみたいになっちゃいますが、
「何に使うのか」を交えて記事にしたいと思います。
…新しい概念を取り落としてたらごめんなさい。(伏線)

2. まずは解説

2.1 モジュール

Elixir Schoolのモジュールの解説から行ってみましょう。

モジュールは関数群を名前空間へと組織する最良の方法です。 関数をまとめることに加えて、関数のレッスンで取り上げた名前付き関数やプライベート関数を定義できます

いきなり飛ばすなぁ。名前空間(namespace)と言う言葉がいきなり出てきますね。このElixir Schoolのスタイル好きよ。
と言うのも、すでに関数の説明の際に、「名前付き関数やプライベート関数」の話をしてますね。

これ以外にも、後で述べる構造体やドキュメントもまとめられるのですが、今はこの二つ、「名前付き関数やプライベート関数」に集中しましょう。

これは、Elixir Schoolのサンプルです。

defmodule Example do
  def greeting(name) do
    "Hello #{name}."
  end
end
iex(2)> Example.greeting "Sean"
"Hello Sean."

Exampleと言うモジュールに、greetingと言う関数が入っています。
greeting、挨拶ですね。まぁ、挨拶するモジュールをたくさん定義することはないでしょう。多分。
では、addだったらどうでしょう?

defmodule Number do
  def add(a, b) do
    a + b
  end
end

defmodule StringOps do
  def add(a, b) do
    a <> b
  end
end

 
Numberが数字を足す、StringOpsが文字列を結合しております。
…こういう時こそ、パターンマッチでやれって?うん、説明だからおいといてね。

こういう時に、名前空間がないと、nunber_add/2とかstrings_add/2とか名前を作って管理する必要があります。
しかし、NumberもStringOpsも別のモジュール、名前空間にいるので、add/2を意識せずに定義出来ています。
使い方は、
モジュール名.関数名ですね。以下の様に使えます。

iex(4)> Number.add(1,3)
4
iex(5)> StringOps.add("test","test2")
"testtest2"

さらに、モジュールはモジュールを含むことも出来ます。
上の例を別のモジュールに含んでみましょう。

defmodule Mylib do
    defmodule Number do
        def add(a, b) do
            a + b
        end
    end

    defmodule StringOps do
        def add(a, b) do
            a <> b
        end
    end
end

使ってみましょう。

iex(4)> Mylib.Number.add(1,3)
4
iex(5)> Mylib.StringOps.add("test","test2")
"testtest2"

名前空間のおかげで、add, Number, StringOpsなどに奇妙な名をつけることなく、関数が呼び出せて
いると思います。まーついでに、「モジュール名.関数名」ですので、「ああ、このモジュールを使ってるんだな」
と明示的にわかるのもよいですねー。

2.2 モジュールの属性(Module Attributes)

さて、モジュールには属性(Attribute, 複数形でAttributes)を定義出来ます。
まずは例を見ましょう。

defmodule Example do
  @greeting "Hello"

  def greeting(name) do
    ~s(#{@greeting} #{name}.)
  end
end

うん、 ~s(#{@greeting} #{name}.)とかサクッと書かれてますね。これ、シジルと言いまして。。。は
あとやりますので、まずは、@名前で定義されている属性、@greetingがどう評価されるか、確認しましょう。

iex(7)> Example.greeting("mokemoke")
"Hello mokemoke."

見事、@greetingは"Hello"に置き換えられていますね。

この属性には、色々と予約されている属性があるそうです。
フレームワークなどでも定義されておりますが、ちょっと一例を引用しておきます。

Attribute 説明
moduledoc モジュールの説明や使用方法を記載します
doc 関数の説明や使用例を記載します。
behaviour モジュールが特定のビヘイビア(振る舞い)を実装していることを示します

とりあえず、こんなもんで。
これはもっと深堀しないと、チートシートにならないですね。

2.3 構造体(Structs)

…落ち着け、オレ、お前の知ってる構造体とは似て非なるものだ。
さて、ElixirSchoolの説明を見てみましょう。

構造体は定義済みのキーの一群とデフォルト値を持つ特殊なマップです。 
モジュール内部で定義されなくてはならず、そのモジュールから名前をとります。
構造体にとっては、モジュール内部で自身しか定義されていないというのもありふれたことです。

サンプルがこちら。defstruct を用い、フィールドとデフォルト値のキーワードリストが添えられております。

defmodule Example.User do
  defstruct name: "Sean", roles: []
end

これ、拡張した「型」の様に使えるんですね…古い理解では、OOPのインスタンスを作成しているノリですかね?
こうやって「作成」します。

iex(9)> %Example.User{} # そのまま、default値
%Example.User{name: "Sean", roles: []}
iex(10)> %Example.User{name: "Steve"} # nameにSteveをセット
%Example.User{name: "Steve", roles: []}
iex(11)> %Example.User{name: "Steve", roles: [:manager]}  # nameにSteveを, rolesに:managerをセット
%Example.User{name: "Steve", roles: [:manager]}

ちょっとわかりづらいのですが、次の例を見てみましょう。
「構造体はあたかもマップのように更新することができます」

iex(12)> steve = %Example.User{name: "Steve"} # name:を"Steve"にして、steveと言う変数にバインド
%Example.User{name: "Steve", roles: []}
iex(13)> steve # steveの中身を確認しております。
%Example.User{name: "Steve", roles: []}
iex(14)> sean = %{steve | name: "Sean"} # steveの名前だけSeanに変更して、seanと言う変数にバインド
%Example.User{name: "Sean", roles: []}
iex(15)> sean
%Example.User{name: "Sean", roles: []}
iex(16

構造体のフィールドに、以下の様にアクセス出来ます。
まー、マップなんだから当然な気もしますが。

iex(18)> sean.name
"Sean"

さて、ここまで忘れないうちに書いておきましょう。

最も重要なことですが、構造体はマップに対してマッチすることができます
iex(16)> %{name: "Sean"} = sean
%Example.User{name: "Sean", roles: []}

2.4 構造体(Structs)のinspect・カスタムイントロスペクション

モジュールの属性の使用例でもあるので、章を分けました。
前の章で作った構造体、seanをinspectして見てみましょう。

iex(20)> inspect(sean)
"%Example.User{name: \"Sean\", roles: []}"

ごっそり中が出てますね。
では、見せたくないものを隠すのに、@deriveを使用します。

defmodule Example.User do
  @derive {Inspect, only: [:name]}
  defstruct name: nil, roles: []
end
iex(24)> sean = %Example.User{name: "Sean"}
#Example.User<name: "Sean", ...>
iex(25)> inspect(sean)
"#Example.User<name: \"Sean\", ...>"

と、アトリビュート@deriveに定義したとおり、:nameのみが表示されております。

さて、本日はこれでおしまい。

3. 本日のチートシート

項目 説明
モジュールとは? モジュールは、関数やマクロ、構造体などをまとめて管理するための単位です。defmoduleで定義します defmodule Example do
def greeting(name) do
"Hello #{name}."
end
end
モジュールはモジュールを含むことも出来ます defmodule Mylib do
defmodule Number do
def add(a, b) do
a + b
end
end
defmodule StringOps do
def add(a, b) do
a <> b
end
end
end
モジュールの属性 モジュールには属性(Attribute, 複数形でAttributes)を定義出来ます。@Attribute名で指定します defmodule Example do
@greeting "Hello"

def greeting(name) do
~s(#{@greeting} #{name}.)
end
end
構造体(Structs)の定義 定義済みのキーの一群とデフォルト値を持つ特殊なマップです。defstruct を用い、フィールドとデフォルト値のキーワードリストが添えます defmodule Example.User do
defstruct name: "Sean", roles: []
end
構造体の作成方法 %Example.User{name: "Steve"}
構造体はマップに対してマッチすることができます iex(16)> %{name: "Sean"} = sean
%Example.User{name: "Sean", roles: []}
構造体(Structs)のinspect・カスタムイントロスペクション defmodule Example.User do
@derive {Inspect, only: [:name]}
defstruct name: nil, roles: []
end
7
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
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?