なんもわからんな

Elixirでベクトルクロックを実装した

Elixirでベクトルクロックを実装致した。 リポジトリは下記にあります。(readmeは後々追加予定)

github.com

使い方は下記の通りです。

# define your processing.
defmodule Tick.Example do
  use Tick.StateMachine

  def increment(n) do
    tick do
      n + 1
    end
  end
end

# make a config to send other nodes.
config = Tick.Config.new(:my_name, [{:my_name, nil}, {:other_node_name, :other_node_name@address}])
# get a spec to generate supervisor.
spec = Tick.Example.child_spec(config)
Supervisor.start_link([spec], strategy: :one_for_one)

# execute block inside `tick`, and send message other nodes.
Tick.Example.increment(0)

記述量も少なく使いやすいものが出来たのではないかと思っています。 実際に動かしたところ

以下説明です。

実装したコード

実装するにあたり、ライブラリ使用者の負担を減らすため少ないコード量で利用できるように心がけました。 コード記述量を減らすためにメタプログラミングでコードを生成する事により、共通的なコードを実装する手間を排除することができました。 メタプログラミングの実装は以下のようになっています。

defmodule Tick.StateMachine do
  defmacro __using__(_opts) do
    quote location: :keep do
      def child_spec(%Tick.Config{}=config) do
        init_opts = %Tick.Server.State{}
          |> Map.put(:current_state, Tick.Server.State.init_current_state(config))
          |> Map.put(:config, config)

        Supervisor.child_spec({Tick.PeerSupervisor, init_opts}, id: Tick.PeerSupervisor)
      end

      defmacrop tick(do: block) do
        quote do
          result = unquote(block)
          Tick.Server.finish_process()
          result
        end
      end

    end
  end
end

__using__マクロ内で定義したものはモジュールの定義になるため、__using__内で書いた関数などは__CALLER__モジュールに展開されます。 つまりuse Tick.StateMachineと書くだけで、child_spec , tick 関数を利用することができます。

次にステートを保持&他ノードへのメッセージの送信をする部分です。 GenServer を用いステートを管理し、メッセージはTask.Supervisorを用い送信しています。 ファイルでいえばここ 特に難しいことはしていないので、説明は割愛で...

感想

やはりElixirで色々実装するのは楽しい、これに尽きます。 簡単にメタプログラミングをすることができ、オブジェクトを作る感覚でプロセスを作ることが出来る...。 今までにないプログラミングの哲学を見ているようで、Elixirでコードを書くことが目的になりそうです。

参考

elixirでのraftの実装になりますが、elixirの(OTP, メタプログラミング)の学習に大変役に立ちました。ありがとうございます。

github.com