Open13

個人的 Elixir関連メモ

Endo ShogoEndo Shogo

個人的にElixirで便利だと思った機能やコード片などを載せていく。
タイトルはTipsとしたが、雑多なメモになっていくはず。
雑多なメモ書きが思ったより多かったためタイトル変更。
Elixirに関する気になったことならなんでも書いていきます。

Endo ShogoEndo Shogo
iex(1)> 1..10 |> Enum.map(&Kernel.**(2, &1))
[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

2の階乗を1~10まで求める。
四則演算はKernelモジュールが持っている関数なので、&記法を使う場合はモジュール名を書く。

Endo ShogoEndo Shogo

Elixirのドキュメントに書かれているKernelモジュールのGuardで利用可能な関数の件数と、
https://hexdocs.pm/elixir/Kernel.html#guards

そのドキュメントの生成元のコードに書かれているGuardで利用可能な件数の数がズレているのはどうしてだろう
https://github.com/elixir-lang/elixir/blob/v1.14.4/lib/elixir/lib/kernel.ex

ドキュメント: 58件
ソースコード: 52件(ブラウザ上で "@doc guard: true" で検索)

Endo ShogoEndo Shogo
 for x <- ,  ["\a", "\b", "\d", "\e", "\f", "\n", "\r", "\s", "\t", "\v"] do: {x, Code.eval_string("?#{x}") |> elem(0) }
[
  {"\a", 7},
  {"\b", 8},
  {"\d", 127},
  {"\e", 27},
  {"\f", 12},
  {"\n", 10},
  {"\r", 13},
  {" ", 32},
  {"\t", 9},
  {"\v", 11}
]

エスケープ文字と各コードポイントの対応表。
空白文字を \s で表現できるのを初めて知った。
?\s の代わりに、?\ (半角空白)でもコードポイントが得られる。

Endo ShogoEndo Shogo
  def before_noon?(datetime) do
    datetime
    |> NaiveDateTime.to_time()
    |> Time.compare(~T[12:00:00Z])
    === :lt

    # 改行を無くすとこの形
    # datetime  |> NaiveDateTime.to_time()  |> Time.compare(~T[12:00:00Z])  === :lt
  end


えっ、これ動くの?と思ったコード。
でも、パイプライン演算子を1列に並べれば納得。
今後は関数の最後に結果を比較して返すようなコードは変数への代入せずに書ける。

Endo ShogoEndo Shogo

なぜElixirなのか?
https://whyelixirlang.com

客観的にElixirを使うべき情報が揃ってて良さそう。
あとでちゃんと読む。

目を通した。
どれも昔の情報しか無いから、サイト自体が古いのかな?
もっと大きいサービスでの活用事例があればいいんだけどねえ。

Endo ShogoEndo Shogo
iex(1)> %{} = %{hoge: 100}
%{hoge: 100}

iex(2)> [] = [hoge: 100]
** (MatchError) no match of right hand side value: [hoge: 100]
    (stdlib 4.3.1) erl_eval.erl:496: :erl_eval.expr/6
    iex:91: (file)

式の左辺に %{} を置いてもマップならマッチングしてしまう。
Listで似たような形で書くとエラーになるため、Mapは気をつけていないと意図しない挙動をしそう。

このElixir Forumでも話題になっていた。
ガード節で判定させるなら、when map == %{} を使うのが良さそう。
https://elixirforum.com/t/pattern-match-on-empty-maps/33259/6

Endo ShogoEndo Shogo
iex(1)> file_types
%{
  bmp: %{
    binary_size: 2,
    common_extension: "bmp",
    media_type: "image/bmp",
    signature: "BM"
  }
}
iex(2)> file_types[:bmp][:signature]
"BM"
iex(3)> text = "BM"
"BM"
iex(4)> case text do
...(4)>   file_types[:bmp][:signature] === text -> true
...(4)>   _ -> false
...(4)> end
** (CompileError) iex:4: cannot invoke remote function Access.get/2 inside a match
    (elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
    (elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
    (elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
    (elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
    (elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
    (elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
    (elixir 1.14.4) src/elixir_expand.erl:591: :elixir_expand.expand_args/3

case の判定の中でMapデータの呼び出しは使えない。他のパターンマッチも同様。
パターンマッチの条件はコンパイル時に埋め込むので実行しないと値が分からないものは条件には使えない。
こういう場合はプロパティを使わなければならない。

Endo ShogoEndo Shogo

Elixirだけの話ではないけれど、リスト内包表記は英語で"List Comprehensions"というらしい。

Endo ShogoEndo Shogo

並行処理: AをしながらBをする、AもBもやる
並列処理: Aをやる人とBをやる人が居る

アクターモデルは、
CPUから見た時は並行処理(複数のプロセスを切り替えながら処理をする)だが、
アクター(?)から見ると並列処理(各プロセスは独立している)
となる

Endo ShogoEndo Shogo
iex> calculator = &(&1) # fn x -> x end と同じ
#Function<42.3316493/1 in :erl_eval.expr/6>
iex> hoge = 10
10
iex> calculator.(hoge)
10

iex> Enum.map(1..3, &(calculator.(hoge)))

** (CompileError) iex:3: invalid args for &, expected one of:

  * &Mod.fun/arity to capture a remote function, such as &Enum.map/2
  * &fun/arity to capture a local or imported function, such as &is_atom/1
  * &some_code(&1, ...) containing at least one argument as &1, such as &List.flatten(&1)

Got: calculator.(hoge)
    (elixir 1.14.4) src/elixir_expand.erl:501: :elixir_expand.expand_fn_capture/4
    (elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
    (elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
    (elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
    (elixir 1.14.4) src/elixir.erl:376: :elixir.quoted_to_erl/4
    (elixir 1.14.4) src/elixir.erl:277: :elixir.eval_forms/4
    (elixir 1.14.4) lib/module/parallel_checker.ex:110: Module.ParallelChecker.verify/1

&記法を利用した中で無名関数の引数呼び出しをすると、呼び出し先の関数が分からなくなるらしい。