iPad Airであそぶ　〜Flowを使ったら速くなるか？〜

やってみたこと
iPad M1はマルチコアなので、Flow使えば速度あがるのでは？という思いつきから
ElixirDesktopを使えばElixirのライブラリFLowが動けば希望が見えるかも
このコラムではmacでiosアプリのビルド方法等には触れない
実験がメインである

ベース↓

https://github.com/elixir-desktop/ios-example-app

こちらに付属するElixirDesktopのTODOアプリを改造してなんちゃってiexを作る

https://github.com/elixir-desktop/desktop-example-app

# ソースを改造する
```diff_elixir:elixir-app/mix.exs
defmodule Todo.MixProject do
  use Mix.Project

  @version "1.0.0"
  def project do
    [
      app: :todo_app,
      version: @version,
      elixir: "~> 1.10",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix | Mix.compilers()],
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      aliases: aliases()
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      mod: {TodoApp, []},
      extra_applications: [
        :logger,
        :ssl,
        :crypto,
        :sasl,
        :tools,
        :inets | extra_applications(Mix.target())
      ]
    ]
  end

  def extra_applications(:host) do
    [:observer]
  end

  def extra_applications(_mobile) do
    []
  end

  defp aliases do
    [
      "assets.deploy": [
        "phx.digest.clean --all",
        "esbuild default --minify",
        "sass default --no-source-map --style=compressed",
        "phx.digest"
      ],
      lint: [
        "compile --warnings-as-errors",
        "format --check-formatted",
        "credo --ignore design"
      ]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    deps_list = [
      {:ecto_sqlite3, "~> 0.8"},
      {:exqlite, github: "elixir-desktop/exqlite", override: true},
      # {:desktop, path: "../desktop"},
      {:desktop, "~> 1.4"},

      # Phoenix
      {:phoenix, "~> 1.6"},
      {:phoenix_live_view, "~> 0.17.11"},
      {:phoenix_html, "~> 3.0"},
      {:phoenix_live_reload, "~> 1.3", only: [:dev]},
      {:gettext, "~> 0.18"},
      {:plug_cowboy, "~> 2.5"},
      {:jason, "~> 1.2"},

      # Assets
      {:esbuild, "~> 0.2", runtime: Mix.env() == :dev},
      {:dart_sass, "~> 0.2", runtime: Mix.env() == :dev},

      # Credo
-     {:credo, "~> 1.5", only: [:dev, :test], runtime: false}
+     {:credo, "~> 1.5", only: [:dev, :test], runtime: false},
+     {:flow, "~> 1.2"}
    ]

    if Mix.target() in [:android, :ios] do
      deps_list ++ [{:wx, "~> 1.0", hex: :bridge, targets: [:android, :ios]}]
    else
      deps_list
    end
  end
end
```

```diff_elixir:elixir-app/lib/todo_web/live/todo_live.ex
defmodule TodoWeb.TodoLive do
  @moduledoc """
    Main live view of our TodoApp. Just allows adding, removing and checking off
    todo items
  """
  use TodoWeb, :live_view

  @impl true

  def mount(_args, _session, socket) do
    todos = TodoApp.Todo.all_todos()
    TodoApp.Todo.subscribe()
-   {:ok, assign(socket, todos: todos)}
+   {:ok, assign(socket, todos: todos) |> assign(exec: "") |> assign(text: "")}
  end

  @impl true
  def handle_info(:changed, socket) do
    todos = TodoApp.Todo.all_todos()
    {:noreply, assign(socket, todos: todos)}
  end

  @impl true
  def handle_event("add", %{"text" => ""}, socket) do
    {:noreply, socket}
  end+

  def handle_event("add", %{"text" => text}, socket) do
-   TodoApp.Todo.add_todo(text, "todo")
-
-   Desktop.Window.show_notification(TodoWindow, "Added todo: #{text}",
-     callback: &notification_event/1
-   )
-
-   {:noreply, socket}
+   exec =
+     Code.eval_string(text)
+     |> elem(0)
+     |> inspect()
+
+   IO.inspect(exec)
+   {:noreply, socket |> assign(exec: exec) |> assign(text: text)}
  end

  def handle_event("toggle", %{"id" => id}, socket) do
    id = String.to_integer(id)
    TodoApp.Todo.toggle_todo(id)
    {:noreply, socket}
  end

  def handle_event("drop", %{"id" => id}, socket) do
    id = String.to_integer(id)
    TodoApp.Todo.drop_todo(id)
    {:noreply, socket}
  end

  def notification_event(action) do
    Desktop.Window.show_notification(TodoWindow, "You did '#{inspect(action)}' me!",
      id: :click,
      type: :warning
    )
  end
end
```

```diff_elixir:elixir-app/lib/todo_web/live/todo_live.html.leex
 <div class="header">
-  <h2><%= gettext "My Todo List" %></h2>
+  <h2>ios iex</h2>
   <form phx-submit="add">
-    <input type="text" name="text" placeholder="<%= gettext "Add new todo item..." %>">
-    <button type="submit">&#8617;</button>
+    <textarea style="height: 300px; width:300px" name="text"><%= @text %></textarea>
+    <button type="submit">Run</button>
   </form>
+  <textarea style="height: 300px; width:300px" name="text"><%= @exec %></textarea>
 </div>

- <ul>
-  <%= for item <- @todos do %>
-  <li phx-click="toggle" phx-value-id="<%= item.id %>" class="<%= item.status %>"
-     ><%= item.text %>
-     <span class="close" phx-click="drop" phx-value-id="<%= item.id %>">&#215;</span>
-     </li>
-   <% end %>
- </ul>
```

# 実行してみた

```elixir:実験用ソース
defmodule Hoge do
  def test_flow() do
    1..10_000_000
    |> Flow.from_enumerable() 
    |> Flow.map(fn x -> x * 2 end)
    |> Enum.sum()
  end

 def test_enum() do
    1..10_000_000 
    |> Enum.map(fn x -> x * 2 end)
    |> Enum.sum()
  end
end

flow = (:timer.tc(fn -> Hoge.test_flow() end) |> elem(0)) / 1_000_000
enum = (:timer.tc(fn -> Hoge.test_enum() end) |> elem(0)) / 1_000_000

[flow,enum, enum / flow]
```

## 実験結果
Flowを使うとこのケースの場合は1.3〜1.4倍動作が速い

![IMG_0006.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/64216768-2781-b912-f707-df57bfa539fa.png)