やってみたこと
iPad M1はマルチコアなので、Flow使えば速度あがるのでは?という思いつきから
ElixirDesktopを使えばElixirのライブラリFLowが動けば希望が見えるかも
このコラムではmacでiosアプリのビルド方法等には触れない
実験がメインである
ベース↓
こちらに付属するElixirDesktopのTODOアプリを改造してなんちゃってiexを作る
ソースを改造する
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
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: ¬ification_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
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">↩</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 %>">×</span>
- </li>
- <% end %>
- </ul>
実行してみた
実験用ソース
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倍動作が速い