Elixir 1.12で増えた関数を調べたい

# はじめに
- [Elixir](https://elixir-lang.org/) 楽しんでいますか:bangbang::bangbang::bangbang:
- 1.12で追加された関数を調べたいとおもいます
- Elixirのバージョン切り替えは、[asdf](https://asdf-vm.com/)を使っています
- この記事は、2021/06/05(土) 00:00〜2021/06/07(月) 23:59 開催の[autoracex #31](https://autoracex.connpass.com/event/215156/)の成果です

# ところで、[Elixir](https://elixir-lang.org/)ってなにに使えるの？
- IoTできます
    - @takasehideki先生の資料
    - [Elixir/Nervesはナウでヤングなcoolなすごいやつ](https://www.slideshare.net/takasehideki/elixirnervescool-249038510)
- Webアプリケーション -> [Phoenix](https://www.phoenixframework.org/)
- AIとか -> [Nx](https://github.com/elixir-nx/nx)

# 方法①
- [公式ページのドキュメント](https://hexdocs.pm/elixir/Kernel.html)をみて、`since 1.12.0`を拾っていく
- 例: [Enum#count_until/2](https://hexdocs.pm/elixir/Enum.html#count_until/2)
![スクリーンショット 2021-06-05 17.43.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/3ec50521-1168-d0c4-1fcc-c584fd1697c1.png)
- スクレイピングしてみるのはおもしろいかもしれません

# 方法②
- それぞれバージョンの違うノードをたてて、`:code.all_loaded()`でそれぞれのノードの結果を比較する
- **今回はこっちでやってみます**
- [プログラミングElixir（第2版）](https://www.ohmsha.co.jp/book/9784274226373/)の223ページをとても参考にしました

## ディレクトリ構成

```
├── node_new
│   └── new.ex
└── node_old
    └── old.ex
```

## node_old

```elixir:node_old/old.ex
defmodule FunctionsAnswer do
  @version "1.11.4"

  def start do
    pid = spawn(__MODULE__, :generator, [])
    :global.register_name(Utils.name(), pid)
  end

  def generator do
    receive do
      {:get, pid} ->
        send(pid, {:get, {Utils.functions(), @version}})
        generator()
    end
  end
end

defmodule Utils do
  @name "awesome"

  def name, do: @name

  def functions do
    :code.all_loaded()
    |> Enum.filter(fn {mod, _} -> "#{mod}" =~ ~r{^[A-Z]} end)
    |> Enum.map(fn {mod, _} -> mod end)
    |> Enum.reduce(%{}, fn mod, acc ->
      Map.put(acc, mod, mod.__info__(:functions))
    end)
  end
end
```

- `functions`関数のところは手前味噌の「[1260 (Elixir 1.11.2-otp-23)](https://qiita.com/torifukukaiou/items/a8f2eb1cf96e9cf385d8)」をご参照ください


```elixir
$ cd node_old
$ asdf local elixir 1.11.4-otp-23
$ asdf local erlang 23.0.1
$ asdf reshim elixir 1.11.4-otp-23
$ asdf reshim erlang 23.0.1
$ iex --sname node_old

iex> c "old.ex"

iex> FunctionsAnswer.start
```

## node_new

```elixir:node_new/new.ex
defmodule Differ do
  @version "1.12.1"

  def start do
    pid = spawn(__MODULE__, :receiver, [])
    send :global.whereis_name(Utils.name()), {:get, pid}
  end

  def receiver do
    receive do
      {:get, {got_functions, got_version}} ->
        diff(got_functions, got_version)
        |> Enum.filter(fn {mod, _} -> "#{mod}" |> String.split(".") |> Enum.count |> Kernel.==(2) end)
        |> Map.new
        |> write_markdown(got_version)
    end
  end

  defp diff(got_functions, got_version) do
    Utils.functions()
    |> Enum.map(fn {mod, functions} ->
      {new_functions, old_functions} = if Version.compare(@version, got_version) == :gt,
        do: {functions, got_functions},
        else: {got_functions, functions}

      old_elixir = Map.get(old_functions, mod, []) |> MapSet.new()
      new_elixir = new_functions |> MapSet.new()
      {mod, [MapSet.difference(new_elixir, old_elixir), MapSet.difference(old_elixir, new_elixir)]}
    end)
    |> Enum.reject(fn {_mod, [only_new, only_old]} -> only_new == MapSet.new and only_old == MapSet.new end)
    |> Map.new
  end

  defp write_markdown(diff_map, got_version) do
    create_markdown(diff_map, got_version)
    |> (&(File.write("output.md", &1))).()
  end

  defp create_markdown(diff_map, got_version) do
    {only_new_index, only_old_index, new_version, old_version} = if Version.compare(@version, got_version) == :gt,
        do: {0, 1, @version, got_version},
        else: {1, 0, @version, got_version}

    """
    # はじめに
    - [Elixir](https://elixir-lang.org/) 楽しんでいますか:bangbang::bangbang::bangbang:
    - この記事では`#{old_version}` => `#{new_version}`の差異をお示しします

    # [#{old_version}](https://hexdocs.pm/elixir/#{old_version}/Kernel.html) => [#{new_version}](https://hexdocs.pm/elixir/#{new_version}/Kernel.html)増えた関数

    #{diff_content(diff_map, only_new_index, new_version)}

    # [#{old_version}](https://hexdocs.pm/elixir/#{old_version}/Kernel.html) => [#{new_version}](https://hexdocs.pm/elixir/#{new_version}/Kernel.html)なくなった関数

    #{if diff_content(diff_map, only_old_index, old_version) == [],
      do: "- 差分なし",
      else: diff_content(diff_map, only_old_index, old_version)}

    # Wrapping up :lgtm::lgtm::lgtm::lgtm:
    - Enjoy [Elixir](https://elixir-lang.org/) :bangbang::bangbang::bangbang:
    """
  end

  defp diff_content(diff_map, index, version) do
    diff_map
    |> Enum.reject(fn {mod, _} -> mod == __MODULE__ end)
    |> Enum.map(fn {mod, diffs} -> {mod, Enum.at(diffs, index)} end)
    |> Enum.map(fn {mod, map_set} ->
      module_name = "#{mod}" |> String.split(".") |> Enum.at(-1)

      diff_functions =
        Enum.reject(map_set, fn {name, _} -> Atom.to_string(name) |> String.starts_with?("__") end)
        |> Enum.map(fn {name, arity} ->
          "- [#{name}/#{arity}](https://hexdocs.pm/elixir/#{version}/#{module_name}.html##{name}/#{arity})"
        end)
        |> Enum.join("\n")

      if diff_functions == "" do
        ""
      else
        """
        ## [#{module_name}](https://hexdocs.pm/elixir/#{version}/#{module_name}.html)

        #{diff_functions}

        """
      end
    end)
    |> Enum.reject(& &1 == "")
  end

end

defmodule Utils do
  @name "awesome"

  def name, do: @name

  def functions do
    :code.all_loaded()
    |> Enum.filter(fn {mod, _} -> "#{mod}" =~ ~r{^[A-Z]} end)
    |> Enum.map(fn {mod, _} -> mod end)
    |> Enum.reduce(%{}, fn mod, acc ->
      Map.put(acc, mod, mod.__info__(:functions))
    end)
  end
end
```

- ちょっと長いですが、`.md`を吐き出すためのゴニョゴニョが長いだけです

```elixir
$ cd node_new
$ asdf local elixir 1.12.1-otp-24
$ asdf local erlang 24.0.2
$ asdf reshim elixir 1.12.1-otp-24
$ asdf reshim erlang 24.0.2
$ iex --sname node_new

iex> Node.connect :"node_old@20-02noMacBook-Pro"

iex> c "new.ex"

iex> Differ.start
```

- [Node.connect/1](https://hexdocs.pm/elixir/Node.html#connect/1)で、`node_old`につないでいます
    - 逆からつないでもいいはずです
- `Differ.start`を実行すると、`output.md`を吐き出します
- 実行結果は別の記事であげます

# Wrapping up :lgtm::lgtm::lgtm::lgtm:
- Enjoy [Elixir](https://elixir-lang.org/) :bangbang::bangbang::bangbang: