# Elixir 触ってみた @ Ruby札幌28 author : ヽ(´・肉・`)ノ # 本人いわく に書いてある Elixir による自己紹介 # 全部が式 iex(1)> defmodule Hello do ...(1)> IO.puts "Defining the function world" ...(1)> ...(1)> def world do ...(1)> IO.puts "Hello World" ...(1)> end ...(1)> ...(1)> IO.puts "Function world defined" ...(1)> end Defining the function world Function world defined iex(2)> Hello.world Hello World :ok # 全部が式(2) - module は沢山の式からなりたっている. - module の内容をプログラムで書ける メタプログラミングできる # メタプログラミングとDSL DSL を簡単に作れる(ExUnitの例) defmodule MathTest do use ExUnit.Case test "can add two numbers" do assert 1 + 1 == 2 end end # protocol によるポリモーフィズム ファイルにも配列にも使えるEnumモジュール Enum.map([1,2,3], fn(x) -> x * 2 end) #=> [2,4,6] # ---- file = File.stream!("README.md") lines = Enum.map(file, fn(line) -> Regex.replace(%r/"/, line, "'") end) File.write("README.md", lines) # protocol によるポリモーフィズム(2) - 自作モジュールでも Enum を使いたい - Enum は Enumerable という protocol があれば使える - MyModule 向けに Enumerable を実装する # protocol によるポリモーフィズム(3) defimpl Enumerable, for: MyModule do def reduce(collection, acc, fun), do # (....) end def member?(collection, value), do # (....) end def count(collection), do # (....) end end # 一級市民としてのドキュメント - 言語レベルでドキュメント化をサポートしている. - 色んなツールで簡単にドキュメントを使える. - マークアップ記法として Markdown を使える. # 一級市民としてのドキュメント(2) defmodule MyModule do @moduledoc """ Documentation for my module. With **formatting**. """ @doc "Hello" def world do "World" end end # 一級市民としてのドキュメント(3) $ iex -r my_module.exs iex(1)> h MyModule # MyModule Documentation for my module. With **formatting**. iex(2)> h MyModule.world * def world() Hello # パターンマッチング まとまっているものをバラバラにして扱いやすくする iex(1)> u = { :user, "John Doe", 19 } {:user, "John Doe", 19} iex(2)> elem u, 1 "John Doe" iex(3)> { type, name ,age } = { :user, "John Doe", 19 } {:user, "John Doe", 19} iex(4)> type :user iex(5)> name "John Doe" iex(6)> age 19 # パターンマッチング(2) ガード節 (when) と混ぜると意図が伝わりやすくなる def serve_drinks({ User, name, age }) when age < 20 do raise "No way #{name}!" end def serve_drinks({ User, name, age }) do # Code that serves drinks! end # ---- serve_drinks User.get("John") #=> Raises "No way John!" if John is under 20 # 隅から隅まで Erlang :application.start(:crypto) :crypto.md5("Using crypto from Erlang OTP") #=> <<192,223,75,115,...>> - バイトコードレベルで互換 - 変換が容易 - Elixir から Erlang の関数はコスト 0 で実行できる # 本人いわく,のまとめ - 全部が式 - メタプログラミングとDSL - protocol によるポリモーフィズム - 一級市民としてのドキュメント - パターンマッチ - 隅から隅まで Erlang # 触ってみたくなった? インストール方法は の 1.1 Installation に書いてある. - Erlang R16B 以降 - Elixir が必要. # 触ってみたくなった?(Mac) brew install elixir で両方インストールできる. # 触ってみたくなった?(Windows) - Erlang: - Elixir: それぞれのコンパイル済 zip をダウンロードして解凍して使うのが簡単でおすすめ ( らしい ) # モダンなプログラミング言語 最近のプログラミング言語が備えている特徴 Elixir も備えている # パッケージ管理 mix :: Ruby の Rake と Bundler を合わせたようなもの - mix new: プロジェクトを作る - mix test: テストを実行する - mix compile: コンパイルする mix --help で詳しくみられる # ライブラリ管理 (みあたらず) - rubygems を操作する gem のようなコマンドはまだ見つけられない - rubygems 相当のライブラリ置き場は というのがある # REPL iex :: Ruby の irb のようなもの iex(1)> "ほげほげ" "ほげほげ" iex(2)> 1 + 1 2 iex(3)> def foo do ...(3)> "foo" ...(3)> end ** (SyntaxError) iex:3: cannot invoke def outside module src/elixir_macros.erl:184: :elixir_macros.translate/2 lists.erl:1339: :lists.mapfoldl/3 src/elixir.erl:134: :elixir.eval_forms/3 iex(3)> defmodule Foo do ...(3)> def bar do ...(3)> "bar" ...(3)> end ...(3)> end iex(4)> Foo.bar "bar" # ユニットテスト ExUnit :: Ruby の Test::Unit みたいなもの defmodule MathTest do use ExUnit.Case test "can add two numbers" do assert 1 + 1 == 2 end end # モダンなプログラミング環境のまとめ - パッケージ管理 - ライブラリ管理(みあたらず) - REPL - ユニットテスト # Elixir らしそうなところ 個人的におおっ! となったところ # マクロ Elixir の内容は全て 3 要素のタプルで表されている - atom か,同じ形式のタプル - メタデータのリスト.ノードの番号とか行番号などを保持する - 呼び出す関数の引数のリストか atom ほとんどの構文がマクロで作られている # マクロ(2) iex(1)> 1 + 2 3 iex(2)> quote do: 1 + 2 {:+, [context: Elixir, import: Kernel], [1, 2]} iex(3)> defmodule MyMacro do ...(3)> defmacro one_plus_two do ...(3)> {:+, [], [1,2]} ...(3)> end ...(3)> end iex(4)> require MyMacro nil iex(5)> MyMacro.one_plus_two 3 # マクロ(3) iex(1)> defmodule MyMacro do ...(1)> defmacro unless(clause, options) do ...(1)> quote do: if(!unquote(clause), unquote(options)) ...(1)> end ...(1)> end iex(2)> nil iex(3)> require MyMacro nil iex(4)> MyMacro.unless true, do: IO.puts "false" nil iex(5)> MyMacro.unless false, do: IO.puts "false" false :ok # 並列 並列があたりまえ. 簡単に作れるようになっている. - spawn : 違うプロセスを作る - x <- y : プロセス x に y という内容を送る - receive : 送られた内容を取得する # 並列(2) iex(1)> current_pid = self #PID<0.26.0> iex(2)> spawn fn -> ...(2)> current_pid <- { :hello, self } ...(2)> end #PID<0.40.0> iex(3)> receive do ...(3)> { :hello, pid } -> ...(3)> IO.puts "Hello from #{inspect(pid)}" ...(3)> end Hello from #PID<0.40.0> :ok # 並列(3) 普通の MacBook で 100 万プロセス生成 16 秒で動くんだぜー $ elixir --erl "+P 1000000" -r chain.exs -e "Chain.run(1_000_000)" {16961375, "Result is 1000000"} # OTP OTPとは何か? - 大抵のプロセスでは,共通の処理がある - パターンを見極めて,共通ライブラリにまとめたもの # OTP(2) OTP の便利なところ(一部) - ワーカープロセスの監視/再起動が **組み込まれている** - ダウンタイム **ゼロ** のリリース,デプロイ # Elixir らしそうなところのまとめ - マクロ (Elixirすごい) - 並列 (Elixirが使っているErlangVMすごい) - OTP (Elixirが使っているErlangのライブラリすごい) # 思考の転換 > プログラマの思考はプログラミング言語に影響される "まつもとゆきひろさん,Rubyに影響を与えた言語とRuby開発初期を語る。 ~ RubyKaigi 2013 基調講演 1日目" # 想像してみてほしい - もし並列処理が簡単に書けるなら - もし無制限に並列処理できるなら どんな考え方をするだろう? Elixir を使って試してみようぜ. # 参考にしている本/サイト - - -