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