トピック
今回で第4回目の勉強会を僕の運営しているコミュニティで開催することができました
清流elixir
connpassでの参加人数+開催直前に新規の1名の方に参加して頂けました
少しずつ賑わってきてる感があって素直に嬉しいです
また参加者の業種や立場がバラバラであるにも関わらず
ユースケース抜きにElixirに興味がある、触ってみたいという感覚が共通しているのが非常に面白い
少なからず色んなケースで需要が見えているということなんですかね(適当
清流elixir-infomation
開催場所: 丸の内(愛知)
参加人数: 3 -> 3
コミュニティ参加人数 : 5 -> 5
20190511現在
第4回の活動内容
いつもは僕がテーマを設定して書籍等、参照しながら
テーマに沿って説明し、余裕があれば手を動かすというスタイルでやっている
何と今回はありがたいことに初回から皆勤参加の現役学生の方が
今回の内容について全て説明をしてくれた
ありがたや...ありがたや...
もう毎回これでいいんじゃないかな(無責任
値の束縛について
Elixirにはそもそも代入という考え方はない
すべては値がマッチするかどうかを確認している
「=」はElixirではマッチ演算子と呼ばれる
a = 1 #代入ではない a = 1 #aは1にマッチする(aは元々宣言がないため)
多くの関数型言語(Haskellやら)では値は束縛される
すなわち、再代入することが出来ない
a = 1 a = 2 #再代入あかん
全く書いたことないけど何となくHaskellで再現できた
main = putStrLn "XXXXXXXX" sub = "aa" sub = "bb" {--Main.hs:5:1: error: Multiple declarations of ‘sub’ --}
実はElixirでは再代入(再マッチ)することが出来る
さっきも普通にaの値を更新してた
え、じゃあ束縛できへんの?となるが
「ピン演算子(^)」を使うことで可能
a = 1 ^a = 2 #マッチしないと怒られる(いいね) #** (MatchError) no match of right hand side value: 2
ピン演算子で値の不変性を保証することが出来る
チームで開発する場合にはピン演算子を使うか使わないかという共通認識を持たなければいけないと思う
何個かピン演算子で遊んでいる内に面白いことに気づいた
#errorにならない a = 2 ^a = 5 - 3 #2 ^a = 4 - a #2 ^a = 2 -a #errr
右側の評価が先に行われて終了した時点でマッチするかどうかを見ているっぽい
なのでこんな風に書けば先ほどerrorになったものも通る(参加者の方が発見、すご)
a = 2 (^a = 2) -a #0
()を使うことで演算の優先度の変更が変わる
色んなデータ型からのマッチ
今まで値をいかに取り出すかという視点で考えていたが
Elixri的にはいかにしてマッチさせるかという視点で考える必要があるということを
再認識して若干反省。1日1マッチを目指して進んでいく
タプル
{a, b} = {1, 2} #a = 1 #b = 2 {a, b, c} = {1, 2} #** (MatchError) no match of right hand side value: {1, 2}
別にこの値を使うことないわって時には「_(アンダースコア)」を使う
{a, _} = {1,2} #a = 1 #_ error {a, _b} = {1, 2} #_b = 2 となるがwarningが出る(アンダースコア消せやって)
マップ
タプルのようにはマッチすることは出来ない
%{a, b} = %{name: "okb", age: 22} #** (CompileError) iex:19: expected key-value pairs in a map, got: a
取り出しかたは5種類ほどあるが過去に説明済みなので
【レポート】第3回清流elixir勉強会in丸の内を開催しました【マップのパターンマッチ】を参照してください
関数でのマッチ
今まで触れるようで触れてなかった部分
自分のブログでも関数の宣言の仕方の解説してないのに当たり前のようにdefmoduleとか使ってて反省
時間あれば書いときます
Elixirでは関数の引数やガード節たるものを使って関数の実行前に関数でマッチすることが可能
そもそも同名の関数を同じモジュール内に宣言することが出来るのがヤバい
defmodule GreetMan do def greet("nobunaga", greet), do: IO.puts("#{greet}, nobunaga") def greet("hideyoshi", greet), do: IO.puts("#{greet}, hideyoshi") def greet(name, greet), do: IO.puts("#{greet}, #{name}") end GreetMan.greet("nobunaga", "hello") #name="nobunaga"にマッチ GreetMan.greet("hideyoshi", "good morning") #name="hideyoshi"にマッチ GreetMan.greet("ieyasu", "bye") #その他(?)でマッチ # hello, nobunaga # good morning, hideyoshi # bye, ieyasu
ただ、これだとnobunagaとか引数渡ししてるのに
IO.putsの中で使用していたりと変更に非常に弱いコードになっている
こんな時に便利なのがガード節
以下のように関数名を宣言した後にwhenと記述するだけ
def greet(name) when name == "nobunage", do
先ほどのモジュールをwhen使って書き直すとこんな感じに
defmodule GreetMan do def greet(name, greet) when name == "nobunaga", do: IO.puts("#{greet}, #{name}") def greet(name, greet) when name == "hideyoshi", do: IO.puts("#{greet}, #{name}") def greet(name, greet), do: IO.puts("#{greet}, #{name}") end GreetMan.greet("nobunaga", "hello") GreetMan.greet("hideyoshi", "good morning") GreetMan.greet("ieyasu", "bye") # hello, nobunaga # good morning, hideyoshi # bye, ieyasu
マッチ後の処理が全く同じなので利便性を感じづらいが関数でマッチできるのはえぐい
タプルとかのデータ構造に対しても関数の引数を使って関数実行前に値をマッチすることが可能
defmodule GreetMan do def is_sucess({:ok, value}), do: IO.puts(value) def is_sucess({:error, reason}), do: IO.puts(reason) end GreetMan.is_sucess({:ok, :good}) #good GreetMan.is_sucess({:error, :bad_value}) #bad_value
再帰やるにはこの関数マッチが必須
なので流れで次回は再帰について触れることになった
次回の開催について
今回新規で参加して頂いた方のご好意で
勤務されている会社の会議室を勉強会で使わせて頂けることになりました(しかも名駅付近)
狭苦しい部屋からは脱出できそうで、本当にありがたや...ありがたや...
つながりを持つのは本当に大事ですね
いきなり広い場所になると緊張しそう
まぁたくさん間違えて恥をかいた分だけ強くなれるのでラフにいきます
内容については「Elixirでの再帰のやり方」にしようと思ってます
詳細はまた追ってconnpassで展開します