趣き重視でElixir Tips

# Elixir Short Tips

(ほぼ)Elixirだけを書いてもうすぐ1年。いくつか短いTipsが浮かんでいるのでまとめる。
自分で書きつつ、コードレビューしつつ、されつつ、今Elixirを書くときに考えるようになった色々なこと。

しかしただ書いてもなんだか味気ないので、「趣き重視」で書く。

# 「あなたは`tuple`を比較する」

- 解説
    - あまりにも基礎的で、ともすれば見過ごしてしまう明快な事実。

    ```ex
    {2016, 12, 31} < {2017, 1, 1}
    # true
    ```

    - `:calendar.date/0`はだから美しい。`:calendar.time/0`もまた美しい。
        - 一方で、このようなデータ構造を我々に作らせようと誘惑する魔とは戦うべきだろう。

        ```ex
        #XXX Do we REALLY need this?
        %{year: 2016, month: 12, day: 31}
        ```

        - コード上のkey順序が意味をなさない`map`では、比較の際にどうなるか、言うまでもない。
        - これだけを見れば相当に愚かな例だが、`map`、そして`struct`に手をかける前、一呼吸入れて損はない。
    - Erlangでは、**「全ては比較できる」**。同質なモノ同士であれば、例えばここでの同サイズの`tuple`のように、その振る舞いは比較的自然に定義されている。
        - さらに言えば、異質なモノ同士を比較しようとしたとき、どのように振る舞うかでさえ[明確に定義されている](http://erlang.org/doc/reference_manual/expressions.html#id81088)。


# 「`Enum`を見よ。`Enum`に求めよ」

- 解説 
    - [`Enum`](http://elixir-lang.org/docs/stable/elixir/Enum.html#sum/1)に無いものは無い。などということは無い。
    - だが`Enum.map/2`がある。[`Enum.map/2`のことを考えない日など有るだろうか？](http://qiita.com/ymtszw/items/443cd95e73406a085a77)
    - そして`Enum.group_by/3`があり、`Enum.map_join/3`があり、`Enum.map_reduce/3`があり、`Enum.partition/2`があり、`Enum.filter_map/3`がある。
    - **数え上げうるものに対して作用したいと思ったとき、`Enum`が応えてくれないことは少ない。**
        - `Enum`が応えてくれないときは、むしろ己を疑ってもいいとさえ思う。
    - リンクした記事でも書いたように、`Enum.reduce/3`は表現力の高い「ビルドブロック」的関数である。その分、実装に使うと読みづらくなりがちだ。
        - `Enum`が用意している関数の中に用途を満たすものがないか、まず探してから`reduce/3`を使わなければならないかどうか考えるべきといえる。


# 「母なるMix」

- 解説
    - `Mix.env/0`や`Mix.Project.config/0`など、Mixから情報を引き出すことはできる。**だがそれができるのはrelease前だけだ**。
        - 今少し正確に言うと、Mixアプリケーションが起動しているときだけだ。
        - `mix test`コマンドでテストしたり(`MIX_ENV=test`)、`iex -S mix`(`MIX_ENV=dev`)したりするとき、暗黙にMixアプリケーションも起動していて、`mix.exs`ファイルの中に書かれたConfig情報などは状態として保持している。
            - `Mix.env == :sys.get_state(Mix.State) |> Map.get(:env)`
        - 当然だ。Mix自身がそれを使ってユーザのアプリケーションを世話してくれるのだから。
    - しかし、アプリケーションを[release](http://erlang.org/doc/design_principles/release_structure.html)の形でまとめた場合は話が変わる。そこには通常、Mix自身は含まれない。
    - ユーザのアプリケーションは、**runtimeに本当に必要となるアプリケーションだけを従えて、独り立ちする**。Mixは送り出すだけだ。母のように。
    - Mixを含まない起動命令に基づけば、以下は実行時エラーになる。

    ```ex
    defmodule M do
      def endpoint_url do
        case Mix.env do
          :prod -> "https://public.endpoint.com"
          _     -> "http://localhost:8080"
        end
      end
    end
    ```

    - コンパイル時に使えば良い。母はそこにいる。

    ```ex
    defmodule M do
      @endpoint_url (case Mix.env do
        :prod -> "https://public.endpoint.com"
        _     -> "http://localhost:8080"
      end)
      
      def endpoint_url: do: @endpoint_url
    end
    ```

# 「`Keyword.t`の美、`map`の罪」

- 解説

    ```ex
    SomeModule.some_fun(prop1: 1, prop2: nil, prop3: "Three")
    ```

    - これは`Keyword.t`[^keyword]、すなわち`[{atom, any}]`を関数に渡すときにのみ許された構文だ。
    - `Keyword.t`はあくまで`list`の特殊型に過ぎず、当然`list`を扱うのと同じくせねばならない。
    - しかし`map`を知ってしまった我々は、時に過ちを犯す。

    ```ex
    defmodule M do
      def list_fun(prop1: p1, prop2: p2) do
        {p1, p2}
      end
      
      def map_fun(%{prop1: p1, prop2: p2}) do
        {p1, p2}
      end
    end

    M.list_fun(prop1: 1, prop2: 2)
    # {1, 2}

    M.list_fun(prop2: 2, prop1: 1)
    # ** (FunctionClauseError) no function clause matching in M.list_fun/1

    M.list_fun(prop1: 1, prop2: 2, prop3: 3)
    # ** (FunctionClauseError) no function clause matching in M.list_fun/1


    M.map_fun(%{prop2: 2, prop1: 1})
    # {1, 2}

    M.map_fun(%{prop1: 1, prop2: 2, prop3: 3})
    # {1, 2}
    ```

    - 実際にここまで愚かなコードを書くことは流石にないだろう。
    - だが、`map`の柔軟さに慣らされた者が荒々しく近づけば、美しい`Keyword.t`は折れてしまうことを知っておかねばならない。
        - ライブラリ関数の末尾引数として`options`のような`Keyword.t`を取ることはままある。省略記法を綺麗に適用できるため、誘惑に駆られる。
        - しかし、翻って関数実装側は`Keyword.t`に対して鮮やかにパターンマッチすることはできない。`Keyword.get/3`の連発になるだろう。[^example]

# 結び

深夜だなあ。
明日は@ikeyasuさん。

[^keyword]: Erlangの`proplists`とは、「関数のオプション指定」といった頻出用途で似ているものの、別物である点には注意。
[^example]: すぐに思いついた例だと、[`Regex.split/3`](https://github.com/elixir-lang/elixir/blob/v1.3.4/lib/elixir/lib/regex.ex#L391)のように。一旦変換してデフォルト値の入った`map`と`Map.merge/2`してからパターンマッチするということも可能ではある。美しいかどうかはわからない。