webネタ

技術系ブログ。web系SE。1988。♂。@rhong_

Part2 : ErlangVMで動くRuby風の関数型言語Elixir(エリクサー) : 基礎編

Part1はこちら

インターフェース

defprotocolでinterfaceのようなものが定義できる

defprotocol Checker do
  def ok?(data)
end

defimplでinterfaceの実装を定義できる

defimpl Checker, for: List do
  def ok?([]), do: "ng"
  def ok?([h | _]) when h == 1, do: "ok"
  def ok?([2 | _]), do: "ok2"
end
defimpl Checker, for: Number do
  def ok?(1), do: "ok"
  def ok?(_), do: "ng"
end
Checker.ok?([1,2,3]) # ok
Checker.ok?([2,3,4]) # ok2
Checker.ok?([3,4,5]) # ng
Checker.ok?(1) # ok
Checker.ok?(2) # ng

関数をキャプチャ

定義済みの関数を、別の関数に渡したいとき等に。

f = &Integer.odd?/1
Enum.map([1,2,3,4,5], f) # [true, false, true, false, true]

Erlangの関数をコール

:(コロン)をつけるとErlangのモジュールが呼べる。

:timer.sleep 1000 # 1秒sleep

プロセス生成

spawnで生成。receiveで外から値を受け取れる。

defmodule Process do
  def from do
    receive do
      i -> 
        IO.inspect i * i
    after
      100 ->
        IO.inspect "timeout.."
    end
  end
end
p = spawn Process, :from, []
p <- 2 # プロセスに値を送るとreceiveで受け取れる。

Stream

scalaにもある無限のリストを作るコレクション

IO.inspect Stream.map(1..1000000000, &(&1 * &1)) |> Enum.take(5) # [1, 4, 9, 16, 25]

File

ファイルをread。

case File.read("/etc/hosts") do
  { :ok, body } -> IO.inspect body
  { :error, reason } -> IO.inspect reason
end

!を付けると例外を飛ばすようになる。rubyっぽい...

body = File.read!("/etc/hosts")
IO.inspect body

例外をcatchしてみる

try do
  File.read! "/etc/hogeeeee"
rescue
  File.Error -> IO.inspect "catche!"
end

様々なファイル操作の関数がある

File.rm_rf! "/tmp/fuga"
File.mkdir! "/tmp/fuga"
File.write! "/tmp/fuga/elixir.txt", "elixir!"
IO.puts File.read! "/tmp/fuga/elixir.txt"
IO.inspect File.ls! "/tmp/fuga"
File.rm! "/tmp/fuga/elixir.txt"

pmap

Elixir関係ないけど、便利なErlangの関数だったので。

1ループを1プロセスで実行するmap。ものすごい並列感...

defmodule Par do
  def exec(i) do
    :timer.sleep 1000
    i * i
  end
end
:rpc.pmap {Par, :exec}, [], [1,2,3,4,5,6,7,8,9] # 1sかかる
Enum.map [1,2,3,4,5,6,7,8,9], &Par.exec/1 # 9sかかる

公式サイトを翻訳したほうが早いんじゃないかと思えてきた...