decadence

個人のメモ帳

Elixirの静的型チェック

Elixir再履修

JVMはすごいけど限界もあるって最近感じてて、process単位でのGCが行えるBEAM系言語としてElixir触ってる*1

前に一度触ったけど文法とか飛んでたしElixir再履修。

エディタ設定とか依存管理とか予め言語開発側が用意してくれているので助かる。ただ動的型付け言語なので、その辺りは後々困るのは確実。Erlangには静的型チェックの仕組みとしてdialyzerがあり、Elixirでも利用可能なのでそのあたりを先に押さえる。

型宣言

when is_number(arg)みたいのを適宜書くのも良いが、そもそもこれはガードに利用するためのもの。Erlangの型付けで利用されていた-type-spec-opaque がそのまま@type@spec@opaqueとして利用出来る。

型についてのドキュメントは以下のページに記述されているので是非読むべき。

さて、実際の例をGitHub - edgurgel/httpoison: Yet Another HTTP client for Elixir powered by hackneyを例に見てみる。

getでは、binary, headers, Keyword.tを引数に取り、{:ok, Response.t | AsyncResponse.t} もしくは {:error, Error.t}を返すという宣言を行っている。

@spec get(binary, headers, Keyword.t) :: {:ok, Response.t | AsyncResponse.t} | {:error, Error.t}
def get(url, headers \\ [], options \\ []),          do: request(:get, url, "", headers, options)

この引数の型にあるbinaryは予め用意された型だが、headersは独自に定義した型である。headers@typeを用いて以下の箇所で[{binary, binary}]として定義されている。defmacro __using__(_)はモジュールをuseした際に自動的に展開されるブロック。

  defmacro __using__(_) do
    quote do
      @type headers :: [{binary, binary}]
    ...

なお、予め用意された型については、Kernel.Typespec – Elixir v1.1.1Built-in typesの項目に記述されている。nonempty_listnon_neg_integerあたりあるのが面白い。

また、構造体の詳細な型付けは以下のように行える。ここでのtHTTPoison.Response.tのことであり、String.tのように様々な構造体やプロトコルの型を表すものとなる*2

defmodule HTTPoison.Response do
  defstruct status_code: nil, body: nil, headers: []
  @type t :: %__MODULE__{status_code: integer, body: binary, headers: list}
end

静的型チェック

ElixirならMixを利用しているはずなので、GitHub - jeremyjh/dialyxir: Mix tasks to simplify use of Dialyzer in Elixir projects.を用いてMix経由で型チェックを行う。

READMEにある通り以下の事を行えば、宣言された型に従い型チェックを行う。簡単。

導入

mix.exs

defp deps do
  [{:dialyxir, "~> 0.3", only: [:dev]}]
end

準備

> mix deps.get && mix deps.compile
> mix dialyzer.plt # 初回及びErlangやElixierのversionが変わった時のみ行う

実行

> mix dialyzer

余談

Stream.tとか使えなくて微妙だ...

__using__について、Phoenixでは以下のようにあって、use HelloPhoenix.Web, :controller みたいに色々マクロを1つのmodule内に定義してた、面白い。

  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end

ref

*1:高級言語しか触ってなくて相変わらずダメだ

*2:http://stackoverflow.com/a/29978255