やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【レポート】第13回清流elixir勉強会を開催しました【Elixirで作成されたOSSのコードを読んでみる会】

トピック

elixir-sr.connpass.com

第13回目の清流elixirの勉強会を開催しました
今回はOSSのコードを読んでElixir力をあげるという目的かつ、弊コミュニティ初の試みとしてリモート(Zoom)での参加にも対応しました
これで全国からのジョインが可能に!! 勉強会の当日は東海と九州が繋がり、事実上の中部から西日本を制覇したことになった。すごい

初のリモート開催のためZoomに手馴れておらず、バタついてしまいました。申し訳ありません。次はもっとスマートに出来るかと思いますので多めに見て頂きたいですm( )m

清流elixir-infomation
開催場所: 丸の内(愛知)
参加人数: 5 -> 8 update!! コミュニティ参加人数 : 16 -> 21 update!
20191026現在

第13回の勉強会の内容について

コードを早く、美しく形に出来るようにするためには、コードを書く経験を積むことも重要だが、達人のコードを読むという経験も非常に重要だと考えている。Elixirの基礎的な文法に関してはある程度、習得が完了したはずなので、Elixirを使って書かれたOSSのコードで実際にどのような記述が現れてくるのかを、1人だと辛いので、集まって読んでみるかという流れに

時間の都合もあるので、コード量が多すぎず、コード量が少なすぎず、かつOSSの内容が比較的分かりやすいものを用いたいということで、私の独断と偏見でTrotというphoenixとは異なるmicro web frameworkのOSSを選択しました

github.com

まず先に得られた結果・知見から

...

俺の知っているElixirと違う...

なんじゃこれ。知らないことばっかりなんですが、それは..
syntaxに関しては概ね把握しているつもりであったし、Enumでパイプ使って脳汁ブシャーッってのも気持ちよく出来るレベルなんで、まぁ読めるやろと思ってたけど、自分のレベルを思い知らされた

特に苦労したのがmacroに関しての理解。存在は知っているし、プログラミングElixirでも扱われているトピックだったので、うっすら頭に知識があるはずだが実際のところ黒魔術的要素が強いのでユースケースまでが想定できていなかった

Trotの内部の至る所にmacroがふんだんかつ、おしゃれに使われており、頭が????となった
最初に遭遇した頭を悩ませた問題のコードは以下になる

./trot/lib/trot/trot.exより抜粋

# module変数 -> 許可するmethod
  @http_methods [:get, :post, :put, :patch, :delete, :options]

  @doc """
  Returns a boolean indicating whether the passed in atom is a valid HTTP method.
  """
  defmacro is_http_method(thing) do
    quote do
      # unquote(module.func) |> is_atom()
      # get, post, patch, delete
      # unquote -> thing -> 実体化(http_methodsのどれかであれば)
      # return -> boolean
      # Q: macroの使うタイミングむずすぎ問題 -> if(macro!!), for(macro!!), def(macro), defmacro(macro) ?????
      is_atom(unquote(thing)) and unquote(thing) in unquote(@http_methods)
    end
  end

このis_http_method(thing)というものがboolの値を返していることは何となく分かるが、なぜここでmacroなのかという疑問は残る
普通にis_atom()の判定と@http_methodsに引数の値が含まれているかどうかが分かれば良いだけではないのか?
なので以下のように書き直しても問題ないはずだが、macroが実行時評価になるという話が関係するのだろうか、いずれにしろ現段階では弱すぎて理解が出来ていないのでレベル上げをしないといけない

defmodule Sample do
  @http_methods [:get, :post, :put, :patch, :delete, :options]
  def is_http_method(thing) do
    is_atom(thing) and thing in @http_methods
  end
end

Sample.is_http_method(:get) |> IO.puts()
# true

このSampleを書いて、TrotのREAD.MEにあるエンドポイントのサンプルを見てみて1つ思ったことは、このget "/hello", do: "hello"というものがmacroでsyntaxを定義しており、この記述が解釈されてAST(構文抽象木)に変換(elixirの場合は3つの値をもつタプルだった気がする)されるみたいな話しが絡んでくるんだろうなぁと。勉強しよう

ちなみに先ほどのdefmacroはこんな感じで呼び出せた

defmodule Trot do
  @http_methods [:get, :post, :put, :patch, :delete, :options]

  @doc """
  Returns a boolean indicating whether the passed in atom is a valid HTTP method.
  """
  defmacro is_http_method(thing) do
    quote do
      # unquote(module.func) |> is_atom()
      # get, post, patch, delete...
      # unquote -> thing -> 実体化(http_methodsのどれかであれば)
      # return -> boolean
      # Q: macroの使うタイミングむずすぎ問題 -> if(macro!!), for(macro!!), def(macro), defmacro(macro) ?????
      # IO.puts(thing)
      IO.puts(unquote(thing))
      is_atom(unquote(thing)) and unquote(thing) in unquote(@http_methods)
    end
  end
end

defmodule Call do
  import Trot
  def exec(), do: is_http_method(:get)
end

Call.exec() |> IO.puts()
# true

知らない機能がたくさんある

Elixirってこんな風になってんの、そんなことができるのって機能が多すぎて驚いた
iexを立ち上げた状態でh Enum(hのあとはモジュール名とかsyntaxとか)としてやるとEnumに関するhelpを確認することが出来る

                                      Enum                                      

Provides a set of algorithms to work with enumerables.

In Elixir, an enumerable is any data type that implements the Enumerable
protocol. Lists ([1, 2, 3]), Maps (%{foo: 1, bar: 2}) and Ranges (1..3) are
common data types used as enumerables:

    iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
    [2, 4, 6]
    
    iex> Enum.sum([1, 2, 3])
    6
    
    iex> Enum.map(1..3, fn x -> x * 2 end)
    [2, 4, 6]
    
    iex> Enum.sum(1..3)
    6
    
    iex> map = %{"a" => 1, "b" => 2}
    iex> Enum.map(map, fn {k, v} -> {k, v * 2} end)
    [{"a", 2}, {"b", 4}]

同じようにh defってやってみるとdefってのがmacroで作られていることが分かる

 defmacro def(call, expr \\ nil)                         

Defines a function with the given name and body.

さらに驚いたのがh defmacroと記述した時。defmacroがdefmacroによって作られている。なんだこれは笑

defmacro defmacro(call, expr \\ nil)  

macroでめちゃ盛り上がる様子

つまりは、Elixirのコードのどこかに本物のdefmacroがいるということになり、今日一上がった。defmacrodefmacroを作っている。うーん。厨二病、素晴らしい...!!

Elixirのsyntaxは多くがmacroで記述されており、ElixirのKernel.exというファイルを見れば確認することが可能だ

github.com

IExとEEx

今まで知ることも触れることもなかったものだ
IExはElixirのiexに関する関数が扱えるようで、IEx.started?とするとiexが立ち上がっているのかどうかがbool値で判定することが出来る
特に感動したのがEExの方で、このEExを使えば色々と面白いことが出来そうだ

EEx.eval_file(ex_file_path)とすると.exに記述されているコードを文字列として取得することが出来る

iex> EEx.eval_file("mix.exs")
"defmodule Trot.Mixfile do\n 
:
:
\"https://github.com/hexedpackets/trot\"},\n     files: ~w(mix.exs README.md LICENSE lib VERSION)]\n  end\nend\n"

当然、文字列なので|>を使ってStringモジュールの関数を使用することが出来る

iex> EEx.eval_file("mix.exs") |> String.at(3)
"m"

あと1つ文字列からコードを実行する関数があったが、何ていう名前だったのかど忘れしてしまった...

福岡のエンジニア強すぎるって

fukuoka.exの発足人のpiacereさんがElixirに関して知らないことがなくて、もう笑うしかない
twitter.com

東海(広すぎるので本当は岐阜と愛知)にElixirの知識をリモートで届けてくださり感謝しかありません
また今回リモートで参加してくださった方は皆さん、福岡の方(間違いありましたら申し訳ありません)で、いかに福岡でElixirに対して注目が集まってるのかが分かるし、議論している話のレベルも高くて驚くばかりだ...

良い感じで悔しい気持ちになれたのでホットなまま、維持していきたい。やる気がめっちゃ出た

総評

コードを一緒に読むのいいですね。自分の弱さ、何が分かっていないかが分かったので良しとする。まだまだ知らないことがあるんやなと高ぶった
こういう思いが出来るのも清流elixirを開催しある程度、継続してきたからだと身にしみる...

なんと10/30(水)にfukuoka.exさんが開催される秋のLT大会にリモートLTという形で登壇させて頂くことになりました!!
リモートLTって凄いな。初めて聞きました笑
半ば強引に枠に入れて頂けたようで恐縮です、ありがとうございます

fukuokaex.connpass.com

「清流elixirのこれまで。なぜ東海でelixirをやるのか」みたいなタイトルでLTします。資料はどこかにアップしようと思うので、ごひいきに〜