Types and specs
- ElixirはTypespecを使い以下の目的に用いる
- カスタムデータ型を定義するとき
- 関数のシグネチャ(仕様)を定義するとき
- Typespecは開発者のためだけのものではない。例えばErlangの静的解析ツールの dialyzer は typespecディレクティブを参照する。
Function specifications
# specの左辺に関数名と引数を、右辺には関数の戻り値の基本型を書く
# listを返す場合は [integer] のようにする。tupleは{:atom, integer} など。
@spec round(number) :: integer
def round(number), do: # implementation...
Defining custom types
@type
を使うとビルトインの基本型の他にカスタムの型を定義することができる
- スコープをprivateにしたいときは
@typep
を使うとよい
defmodule LousyCalculator do
# カスタム型の説明と定義
@typedoc """
Just a number followed by a string.
"""
@type number_with_offense :: {number, String.t}
# returnにカスタム型を指定できる
@spec add(number, number) :: number_with_offense
def add(x, y), do: {x + y, "You need a calculator to do that?!"}
@spec multiply(number, number) :: number_with_offense
def multiply(x, y), do: {x * y, "Jeez, come on!"}
end
defmodule PoliteCalculator do
@spec add(number, number) :: number
def add(x, y), do: make_polite(LousyCalculator.add(x, y))
# 引数の型としてカスタム型を指定している
@spec make_polite(LousyCalculator.number_with_offense) :: number
defp make_polite({num, _offense}), do: num
end
Behaviours
- 多くのモジュールはAPIを共有している。例えばPlugの個々のモジュールにはinit/1とcall/2が実装されている。
- Behaviourは以下の目的のために定義される。Javaにおけるinterfaceのようなもの。
- モジュールによって実装しなければいけない関数を定義する
- 必要な関数がすべて実装されていることを保証する
Defining behaviours
- 例えばJSONとYAMLのParserを書きたいとき、Parserとしては同じ動きで扱うものが違うだけなので共通のインタフェースを作ったほうがよい。まずはParserというbehaviourを定義する
defmodule Parser do
# behaviour をrequireして __using__ を呼び出して初期設定を行う
use Behaviour
# defcallbackで実装しなければいけない関数を定義。
# defcallbackで定義した関数はbehaviourを実装したモジュール内ですべて定義する必要がある
defcallback parse(String.t) :: any
defcallback extensions() :: [String.t]
end
defmodule JSONParser do
@behaviour Parser
def parse(str), do: # ... parse JSON
def extensions, do: ["json"]
end
defmodule YAMLParser do
@behaviour Parser
def parse(str), do: # ... parse YAML
def extensions, do: ["yml"]
end