[Elixir]EctoのQueryを少し読み解く ~ コードに前提条件が書かれるので読み解きやすいですね ~

Ectoは、Elixirが標準として提供するDBへの接続ライブラリです。macroを学んだので、少しmacroを読んでみよう、ということで読んでみました。 ここでは、すべてEcto v0.16.0を元にしています。 Ecto.Queryの補完を見ればわかるのですが、この下にfromなどのSQL構文を持っています。 これらを使い、例えば、 Sample.User というリポジトリに含まれる要素に対してSQLを発行したい場合に以下のようにSQLを発行することができます。 これらを読んでいこうとすると、まずは以下の fromに関するdefmacro に出会います。 defmacro from(expr, kw \\ []) do コード このコードを読んでいくと、このmacroの先で幾つかの条件に分けれられた from が定義されています。 defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @binds do コード defp from([{type, expr}|t], env, count_bind, quoted, binds) when type in @no_binds do コード defp from([{join, expr}|t], env, count_bind, quoted, binds) when join…More

[Elixir]macroのテストを書いてみる

macroのテストを書く macroのテストでは、通常は コンパイルされたモジュールの振る舞い コンパイルされた結果(AST)自体 の2つの側面をテスト対象と見てテストコードを書いていくことになります。 振る舞いに関しては、以下のような感じでExUnitを使います。こちらは、普通にmacroによって定義された関数を使って、期待するinput/outputを確認する、というものです。 コンパイル自体に対しては、以下のようにAST自体が正しくできているか?を確認することになります。他は↑とさほど変わりません。inputがASTの文字列、outputがassertionになります。 例えば、以下のような内容になります。 少し話が逸れますが、Agentなどのテストにおいて、Processにsendされた値を確認したい時があります。つまり、mailboxの中身を確認したい。そんな時は以下のように __mailbox__ を使うことができます。 いつMacroを使うか? より簡潔にコードを記述できる場合 コード生成が必要な場合 例えば、Phoenixのルーティングではコード生成によってGET/POST/PUT/PATCH/DELETEなどの match を定義しています。 このような、少量のコードを書くことで、大量の汎用的なコードを生成する、というような場合、それら全てを手で書くよりも正しく、効率的に実行コードを生成できます ただし、複雑になると理解が難しくなるので、metaprogramingで大事なことは、simpleであることです。 mix-inするとき、 import を使えるなら use は使わない use を使うと、 __using__ を読み込みます。そのため、mix-inとしてimportのように使うことができます。ただ、macroをmix-inのためだけに使うのは止めましょう。単に複雑さを増すので。 macroを使ったmix-in: NO importを使ったmix-in: OK 他、いろいろありましたが大事なところはここら辺かな。 ここまでの内容は、『Metaprogramming Elixir』を読んだ内容が主でした。 __using__ の使い所とか、テストフレームワークとか作るのに必要なメタプログラミングの知見を得られたかな、という感じで、良い学びでした。ついでに、ちょくちょくElixir関連のOSSに貢献できたので、それも良かったかな 🙂 関連 [Elixir]macroを書くときの影響範囲 [Elixir]Macroで簡単なテストフレームワークを作ってみるMore

[Elixir]Macroで簡単なテストフレームワークを作ってみる

テストフレームワークと書きながら、そこは余り多く書いていませんが… Elixirは、 Elixir is small because it doesn’t have ti include all common features. とのこと。なので、他に必要な拡張が欲しければ、自分で作ってねという言語らしい。基本は小さく、という立ち方ですね。 bind_quoted 通常、 quote で囲まれたところのうち、変数をそのまま使いたい場合は unquote します。ただ、都度 unquote するのは少し面倒だし、場合によっては意図しない動作になります。その問題を解決するために、 bind_quote があります。 例えば、以下のASTが bind_quote の有無でどう変わるのかを覗いてみます。 unquoteのみ bindされたとき bind_quoteのときは、最初に処理が関連付けられて、それを以降のquoteでaliasのようにして呼び出している、という処理をするようですね。unquoteの有無で意味が大きく異なる処理もあるので、これは良さそうです。 このbind_quoteが有効な時は、unquoteはその範囲内では無効に設定されます。 なお、このほかにも https://github.com/elixir-lang/elixir/blob/f7183440716d705bfaabdc54c216d87cebacd9a7/lib/elixir/src/elixir_exp.erl#L146 にある、 がquoteのときにしていできる、特別なoptionのようです。 テストフレームワーク 簡単なテストフレームワークを書きました。以下リポジトリに突っ込んでいます。 内容自体は簡単で、 assert の実装 test で始まるメソッドをテストケースとして認識、実施する というものです。 https://github.com/KazuCocoa/my_mini_ex_test_assertion/tree/master サンプルを見ながらの実装ですが、これはそんなに難しくないです。 Compile-Time code generation コンパイル時にコードを生成、メソッドなんかで定義されるmacroの話です。 unquoteが、quoteの外で使われている場合があります。 これにより、以下のように動的な関数を定義することができる。 ここで、Elixirは、外部ファイルに依存している箇所を明示することができる。 これにより、Elixirは mimes_path…More

[Elixir]macroを書くときの影響範囲

ElixirのASTって、Lipsの文法をTupleで表現しているようなものなのですね。defmacro、quoteをMacro.expand_onceなんかで分解してみると、 {atom, [List], [List]}の形式で分解されていき、Lipsのような読み方で読み解くことができる、と。 なので、Elixirは Elixirの文法そのままでHigh Levelな表現で処理を記述する LipsのようなLow Levelな表現で処理系を記述する という2層で処理を記述することができる、と。High/Lowは、処理の抽象度を指しています。 後者がMacroになるわけですが、そんなMacroも下手に適用範囲を広げないためにcontextによる適用範囲を限定する機能を持っています。 Elixir Hygiene 以下の通り、 __MODULE__ により、contextが異なることがわかります。 quote で囲まれるところは、つまるところ、呼び出された側のcontextに限定された環境下でのみ、有効になる、ということを意味する。 Elixirはこのようにcontextを設定することで、macroの影響範囲を限定する手段をとっています。 Violate hygiene!! 通常、 quote で囲まれた範囲は、quoteで囲まれた中だけで有効です。 それ以外の領域には影響を及ぼすことはありません。 例えば、以下の通り name の変数は Setter.bind_name のマクロの範囲を出ることはありません。 しかし、以下のように var!/1 を使うとこのhygieneな領域を超えて変数をOverrideすることが可能になります。この機能は、本当に必要なときのみ使いましょう、と強く書かれてもいます。 他メモ Logger.debugは、productionのコンパイルが行われるときに削除される。 なるほど… Macroは処理系を書き換えてしまって、例えば Kernel.if でさえ異なる処理に書き換えることもできるので少し慎重になっていましたが、影響範囲を限定的にできる、その領域を超えるときは明示が必要、という姿勢が良いですね。 ElixirのMacroのドキュメントに以下が書かれていることからも、やっぱり明示に寄せている方に寄せておきたいですね。 Remember that explicit is better than implicit. Clear code is better than concise code. 追記…More

『Programming Elixir』を読んで、写経とかした

読む順番、何か間違えているような気もしますが… Programming Elixirを読みました。 以下、メモです。 読んでて、まだ知らなかったとか、なるほどなーと感じたところだけ… nestしたstructにアクセスする便利な機能 ちなみに、update_in を使うと、 のように関数を与えることもできる。 さらに、他にもmacroなど用意されてたり、更新する値を [:a, :b, :c] のような記述でkeyを指定できたりと、思いの外奥が深い… Stream関連 IO.streamの中身(Elixir 1.0.5時点) __build__ が何かなーと思うと… なるほどー。何か大層なことしていると思ったのですが、構造体に突っ込んでいるだけなのですね。 StreamはLazy Enumerablesなので、その処理対象が必要になった時に値を処理したい、という時に有効なのですね。 Comprehensions 以下のようにElixirでは記述することができるのですが、この for 、いわゆるループのforだと認識していたのですが、 ふと考えてみると ~ for you とかの、~に対してという感じのforとして使っているのですね。元々そうなのかな。。。そう見えると、この記述、なるほどなーという感じ。 Char lists [a | b] はリストの結合。なので、以下は ‘cat’ というchar_listに対して、 ‘dog’ という別のリストを結合する、という処理になるので、以下のような形になるのかな。 例えば、以下のような感じ。ここで a のリストがそのまま ‘cat’ に該当するという。 escript 以下のように escript を指定することで、mix実行時にコマンドを実行することができる。 Cuoncurrent programingの話 processの生成とその経過時間 プロセスに処理を渡すspawnは、 Kernel、Nodeとあって、それぞれがspawn、spawn_link、spawn_monitorなど、種類とその引数のバリエーションがあってパッとこれだ!と思い浮かばない……More

Ecto0.16.0でvalidate_uniqueがdeprecateになって、unique_constraintが追加された

これは、 Changeset の構造体に constraint の要素を設定し、DBへの処理を行うタイミングでその束縛に反していないかを確認する、というもののようです。 変更のあったコミット https://github.com/elixir-lang/ecto/commit/40c7edb69de8a30145e4e4d54cc8108cb1999630 これをざーっと眺めると、 lib/ecto/changeset.ex の moduledoc が更新されていることがわかります。 constraints と validation の関係が記載されているので、気になるかたは眺めてみてください。 However, constraints can only be checked in a safe way when performing the operation in the database. As consequence, validations are always checked before constraints. Constraints won’t even be checked in case validations failed. とのことです。 changeset.ex の defstruct に以下が追加されています。…More

『Elixir in Action』を読んで写経とかも合わせてやった

Elixir in Actionは http://www.manning.com/juric/ http://www.amazon.co.jp/Elixir-Action-Sasa-Juric/dp/161729201X ここら辺で買えるやつです。 motivation 大学院の頃、Byzantine problemを題材に扱ってました。その主な対象が、現実問題だと分散システムやその信頼性だったこともあって、比較的、分散環境に対するシステム的な話に、技術的な興味を持っています。 今のWeb/Mobileなど含んだ様々な端末から構成されるサービスだったり、例えば、 The Wisdom of Crowds という、いっときかなり有名だった「集合知」に関する話題なんか。そういったものも分散システムですね。 ここ数年の間、Erlangは興味あったけれど、学ぶほどまでモチベーションがわかなかった…(多少、あることをきっかけにかじった程度) そんな折、Elixirを知って、Getting Statedをざーっと通して面白みを感じたので学んでみました。そして、興味の対象の理解に支障が無いくらいの書き物を身につけて、Elixir in Actionに手を出してみました。 成果 ざっと、書きながら、読みながら、有した時間は2週間程度でしょうか。だいたい、きりがよい段階でBlogに簡単なメモ残して、トントン、と。 読む前の能力としては、以下をざーとやったあと、のものです。 Getting Startedを一通りやった Hex.comに数個ライブラリをあげてみた Phoenixを使った、簡単なWebアプリを開発してみた EctoやPhoenixに本当にちょっとしたPRを送ってみた 以下、Blogを列挙。 [Elixir in Action]No technology is a silver bullet. [Elixir in Action]Read building blocks [Elixir in Action]Erlang/Elixirの再帰計算におけるnon-tail recursionとtail recursion [Elixir in Action]Guard clauseの優先度 [Elixir in Action]polymorphismで拡張していく [Elixir…More

[Elixir in Action]Elixirで分散システムを構成するための諸要素を知る

ここら辺がこの書籍の真骨頂でしょうか。 Chapter11ではよく知られたOTP applicationを作るためのファイル構成とか、書き方な話なのですっ飛ばして、Chapter12のメモ。 ざっと読んで、触った感覚としては、分散システムは無知では手を出さないほうが良いかなーということ。色々考慮漏れで溢れそうな予感… Erlangベースのシステムでは、processとmessageによって分散システムを構成します。(伝統的なRPCと混同しないように) BEAMによる分散システムは、複数のnodeがそれぞれ接続されてクラスタ化されることで実現されます。このnodeは、BEAMインスタンスと呼ばれます。 Nodeの接続 以下のようにして、簡単なnodeを立てて接続、クラスタを組むことができます。 node1 node2 node3 node1とnode2の後に以下を実施。 Clusterをすでに構築しているnodeに接続すると、自動的にそのCluster内の他nodeとの接続も行われます。これは、tick messageと呼ばれるメッセージのやり取りが行われるためです。これにより、Clusterに含まれるnodeの生存確認も行います。すでにdisconnectな状態のnodeがあれば、それは Node.list から除かれます。 Standard I/Oの実行と出力先 以下の通り、node1で実行した内容を、 Node.spawn でnode2に渡すと、その処理はnode2で行われ、結果をnode1で表示する、ということができます。 これは、すべてのstandard I/Oの出力はClusterのgroup leaderに渡されるためです。group leaderは、処理をinputされたnodeで、ここではnode1を指します。 ここで送るmessageには特に制限はないとのこと。このmessageは、 :erlang.termi_to_binary/1 でエンコードして送られて、受け取ったnodeは :erlang.binary_to_term/1 でデコードするそうです。 Cluster構成の前に nodeが互いにやり取りを行うにあたり、必ず以下の操作が必要になります。(送信元をclient、送信先をserverと表現) ClientがServerのPIDを取得する ClientがServerにmessageを送る そのため、まずはPIDを取得する必要があります。 PIDのlookupは基本的にlocalで行われます。そのため、目的のprocessを自身のlocalで見つけること自体は高速に行われます。 PIDにはルールがあって、以下のようになっています。 どのnode上にあるprocessか(localなら、ゼロ) local内でユニークなnodeの番号 ↑のnodeの番号が表現可能な範囲を超えると増加する Global関数を使って広範囲でClusterを構成する Global関数を使って、globalな領域でclusterを組むことができます。このglobalは、Erlangですでに用意されている関数です。 Elixirだと、GenServerなんかで start_link するときとか、 :global 指定で起動することができます。 https://github.com/elixir-lang/elixir/blob/e74852ffcc95872915be2b1aae453e74d6c54325/lib/elixir/lib/gen_server.ex#L132 実際にglobalを使うと、以下のような形でregisterとwhereisでPIDを得ることができます。 以下のように、なんらかの他nodeとリンクを張っていると以下のような情報が取得できます。 pg2関数を使ってグループに分ける :pg2関数を使ってグループを作ることもできます。このpg2関数では、同じエイリアス( :doro_list というところ)に複数のnodeをぶら下げることにより、1つのエイリアスに対して複数のnodeをグループ化できます。 ここでは、node1で元となるprocessを作り、そこにnode2とnode1が順に参加、グループとなる例を示しています。…More

[Elixir in Action]Erlang Term Storageを知る

Getting Startedなどやっているとよく出てくる、定番な機能ですね。ETSは Erlang Term Storage の略です。 ある1つのprocessをキャッシュ代わりに使っている場合、そのprocessの性能や拡張性が処理のボトルネックになることが多くなります。そんなときの解決策としてETSが説明されています。(ETSに限らず、何かの要素と依存関係を持つ場合、その要素のいずれかがボトルネックになることは多々ありますね。) 特徴 Positive etsは、owner processが起動している間だけ使える(in-memoryで) serialize(連続的に)なキャッシュに書き込める 分けられたメモリ空間に書き込まれ、多くのprocessから並行にアクセス可能 globalでアクセス可能 mutable 非常に高速なアクセスが可能 末尾に内容を記載 Negative client processとETS tableはコピーされる ETSに保存されるデータが原因でprocessがcrashする場合、 Supervisorによってリカバリされたprocessも再びcrashする という、悪循環に陥ることがある BEAMの良さであるリカバリシステムを崩す原因になる ETSには、複雑で大きなデータは扱うべきではない ETSはネットワーク越しの他BEAMインスタンスとは共有できない 本当に性能/拡張性を改善したいときだけにするといった用途を制限するほうが良い 高い性能で多くのprcessで共有したいデータか?など ETSの使い方 New http://www.erlang.org/doc/man/ets.html#new-2 table types :set The table is a set table – one key, one object, no order among objects. This is the default table…More

[Elixir in Action]Supervision Tree ~ ネストされたSupervisor ~

メモメモ。 前回では、Supervisorというprocessの説明でした。 ここからは、エラーに関する話やSupervision Treeの話。 Supervision treesは、Supervisorによって以下見たく構成されるSupervisor – processの関係性を指します。Supervisorやworkerがlinkの関係を持ち、strategyに沿ってそれぞれのprocessを監視していきます。 Supervisorには、その子processの起動として、以下を定義しています。 Supervisorは、親から子とprocessを順に起動していきます。このprocessの起動は同期的に行われます。なので、上記では worker(Todo.ProcessRegistry, []) => supervisor(Todo.Database, [“./persist/”]) => … という順に前のprocess起動が終わるのを待って次のprocessが起動していきます。 これらのworkerやsupervisorは、 Sample.Supervisor のprocessが終了するとterminateされていきます。(これがsupervisorと、その子の関係ですね) Erlang/Elixirが提供する Supervisor では、 link や monitor をPIDによって指定したプロセスを対象として監視していたものを、抽象化して、processにひも付けたatomによって監視します。これは、PIDはprocessがcrashした後に起動するたびに変わるためですね。 前回にもメモしていましたが、processの監視には link と monitor があります。子processがcrashしたらそれ単体を再起動する、などしたいならlink、子processがcrashしたらそれに関係する他の子processも終了する、とか実装したいならmonitorを使う必要があります。 Supervisorには、その子processのcrash時の挙動として幾つかのstrategyを定義し、提供しています。それらから必要なstrategyを設定して、実際には運用していくことになります。この選択はfault toleranceなシステムを構築するうえで重要な要素。 :one_for_one – if a child process terminates, only that process is restarted. :one_for_all – if a child process terminates, all…More