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.1のBuilt-in typesの項目に記述されている。nonempty_listやnon_neg_integerあたりあるのが面白い。
また、構造体の詳細な型付けは以下のように行える。ここでのtはHTTPoison.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