elixirだ 第1回 - 基礎だ -

61
第1回 - 基礎だ - GMO Pepabo, Inc. Joe Honzawa 2015/4/30 Elixir勉強会 Elixirだ

Upload: joenoh

Post on 21-Apr-2017

5.416 views

Category:

Engineering


3 download

TRANSCRIPT

第1回 - 基礎だ -

GMO Pepabo, Inc. Joe Honzawa

2015/4/30 Elixir勉強会

Elixirだ

Elixirとは> Erlang VM上で動く関数型言語 > 並列分散 > 高可用性

> 動的型付け > モダンな文法 > マクロでメタプログラミング > プロトコル

Elixirをつくった人

>José Valim

> 歩く生産性

> rackやrailsにもコミット

Rubyistの皆さんが 入りやすそうなとこから

Install

> $ brew install elixir

IEx> $ iex

Types(?)> integer > float > atom > boolean > binary > string

> list > tuple > map > range > function など

Integer> 10 > 0b101 > 0o12 > 0x0a

Float> 1.0 > 3.14 > 6.02e23 > 1.602e-19 > 1e3 ← これダメ

Atom> Rubyで言うSymbol > :atom > :hello > :’white spaces !?’ > nil == :nil

Boolean> true > false

> 実はatom > :true > :false

Binary> <<1>> > <<256>> == <<0>> > <<1>> == <<1::size(8)>> > <<1, 0>> == <<256::size(16)>> > <<1::float>> == <<63,240,0,0,0,0,0,0>>

String> “hello world” > “世界を征服だ”

>必ずダブルクォートしてー > “hey” == <<104,101,121>> > ‘hey’ == [104,101,121]

List> 単方向リスト > [1, 2, 3] > [true, [1, 2, 3], “hey”] > [1 | [2, 3]] == [1, 2, 3] > [1, 2 | []] == [1, 2]

> 先頭への要素追加は高速

Tuple> 値の組 > {:ok, 30} > {1, 2, 3, 4, 5}

> ランダムアクセスはO(1) > 更新や追加はコスト高

Map> %{} > %{“price”=> 3980} > map = %{price: 100, amount: 2}

> map.price > Map.get(map, :price)

> map = Map.put(map, :amount, 1)

Range> 範囲 > 1..3 > 4..-5 > 1.2 .. 3.8

> (1.5 in 1.2 .. 3.8) == true

Function>無名関数 > fn (x, y) -> x * y end > fun = &(&1 * &2) > fun.(3, 4) == 12

>定義済み関数のキャプチャ > IOモジュールのputs関数(arity: 1) > Enum.each([1, 2], &IO.puts/1)

制御構文

casecase the_number do 1 -> “one” 2 -> “two” 3 -> “three” end

ifif now == :morning do “good morning” else “hi” end

condcond do rem(y, 400) == 0 -> “Yes” rem(y, 100) == 0 -> “No” rem(y, 4) == 0 -> “Yes” true -> “No” end

無いもの> return > 最後の評価値が返る > loop, while > 再帰で

パターンマッチ

賢く束縛> [x, y] = [1, 2] > x == 1 > y == 2

> {1, x, 3} = {1, 2, 3} > x == 2

アンスコ> 使わない変数の頭につける > 何にでもマッチ > 値は要らない

> [head | _] = [1, 2, 3] > {y, _m, _d} = {2015, 4, 30}

代表的な> {:ok, res} = File.read “a.txt”

> [head | tail] = [1, 2, 3, 4] > head == 1 > tail == [2, 3, 4]

実際的なcase File.read(“a.txt”) do {:ok, res} -> res {:error, :enoent} -> “oh it isn’t here” {:error, :eacces} -> “you can’t read it” _ -> “?” end

MatchError> {:ok, res} = File.read “a.txt”

> File.read/1 > {:ok, content} > {:error, reason}

> (MatchError) no match of right hand side value: {:error, :enoent}

ピン止め> x = 1

> [^x, y, 3] = [1, 2, 3] > y = 2

> [^x, y, 3] = [5, 4, 3] > MatchError

モジュールと関数

defdefmodule MyModule do def print(arg) do iikanjini_hyoji(arg) end

defp iikanjini_hyoji(arg) do IO.inspect arg end end

MyModule.print %{price: 298}

原則> あらゆる関数はモジュールに属する > 名前かarityが違うなら別の関数 > sum/1とsum/2は別物

重要なスライドですdefmodule MyModule.Math do def sum(list), do: sum(list, 0)

defp sum([], acc), do: acc

defp sum([h|t], acc) do sum(t, h+acc) end end

慣習> ワーカ関数の名前はdo_xxx > モジュール名とファイル構造は対応させる > lib/my_module/math.ex > MyModule.Math

> 最も関心のあるものを第一引数に > 例外を投げる関数の名前はxxx! > 真偽値を返す関数の名前はxxx?

デフォルト引数defmodule MyModule do def say(word \\ “hi”) do IO.puts word end end

MyModule.say(“hello”) MyModule.say

ガード式defmodule MyModule do def sum(list) when is_list(list) do sum(list, 0) end

# 略 end

!! ガード式で使える関数は限られている !!

ガードで使える奴ら> == > != > === > !== > > > < > <= > >= > and > or > not > !

> + > - > * > / > <> (左辺がリテラル) > ++ (左辺がリテラル) > in > is_atom/1 > is_binary/1 > is_bitstring/1 > is_boolean/1 > is_float/1 > is_function/1

> is_function/2 > is_integer/1 > is_list/1 > is_map/1 > is_nil/1 > is_number/1 > is_pid/1 > is_port/1 > is_reference/1 > is_tuple/1 > abs/1 > bit_size/1 > byte_size/1

> div/2 > elem2 > hd/1 > length/1 > map_size/1 > node/0 > node/1 > rem/2 > round/1 > self/0 > tl/1 > trunc/1 > tuple_size/1

directivesdefmodule MyModule do import Enum

alias Enum, as: E

require Integer

use ExUnit end

importdefmodule MyModule do import Enum, only: [count: 1]

def kazoeru(list) do count(list) # Enum.count/1 end end

MyModule.kazoeru [1, 2, 3] MyModule.count [1, 2] → ❌

aliasdefmodule MyModule do alias Enum, as: E

def kazoeru(list) do E.count(list) # Enum.count/1 end end

MyModule.kazoeru [1, 2, 3]

as無しaliasdefmodule MyModule do alias Phoenix.HTML.Form alias Phoenix.HTML.Form, as: Form

# 等価 end

requireとuse

また後で…

エコシステム

Mix> Rubyにおけるbundlerとrake > 依存性解決 > タスクランナー > mix new my_app > mix test > mix something.i.defined

Hex> Rubyにおけるrubygems > パッケージマネージャ > https://hex.pm > ErlangのライブラリもOK > ファイル構成見てよしなにシュッと

Hex on Heroku

はじめての Elixirプロジェクト

$ mix new please $ cd ./please $ ls

Mix project> lib/please.ex > test/ > please_test.exs > test_helper.exs

> config/config.exs > mix.exs

lib/please.exdefmodule Please do def give_me(:sushi), do: “🍣” def give_me(:beer), do: “🍺” def give_me(_other), do: “❓” end

$ iex -S mix

iex(1)> Please.give_me :sushi “🍣”

余談> BEAMファイル >バイトコード > JavaでいうClassファイル > _build/の奥のほう > Elixir.Please.beam

テスト書きましょ

テストライブラリ> ExUnit > とりあえずこれ覚えるべき

> ShouldI > Bruce Tateさん作 > “One Experiment, Multiple Measurements” > 中身はExUnit

> ESpec

test/please_test.exsdefmodule PleaseTest do use ExUnit.Case alias Please, as: Plz

test “sushi” do assert Plz.give_me(:sushi) == ”🍣” end

test “neither sushi nor beer” do assert Plz.give_me(:hoge) == ”❓” end end

$ mix test

Redも見ておきましょうdefmodule PleaseTest do use ExUnit.Case alias Please, as: Plz

test “sushi” do assert Plz.give_me(:sushi) == ”🍕” end

test “neither sushi nor beer” do assert Plz.give_me(:hoge) == ”❓” end end

ワンダーである

やったこと> Elixirのインストール > 型 > 制御構文case, if, cond > パターンマッチ > モジュール、関数、ディレクティブ > MixとHex > Mixプロジェクトの作成とテスト