Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【Crystal】Macroについて

 【Crystal】Macroについて

東京 Crystal 勉強会 #6 in 渋谷
https://crystal.connpass.com/event/90745/

イベント資料です。

3d1621a44968b7b2f7cf8907b3a3645b?s=128

at_grandpa

June 21, 2018
Tweet

Transcript

  1. Macroについて 2018.06.21 @at_grandpa Crystal 勉強会 #6 in 渋谷

  2. @at_grandpa

  3. 圧倒亭グランパのブログ

  4. Crystal Advent Calendar 2017

  5. Crystal Advent Calendar 2017

  6. Macroについて

  7. ✔ Macro の雰囲気を話します ✔ 細かい syntax などはドキュメント参照

  8. None
  9. ✔ 「Crystalのコード」を書くコード ✔ コンパイル前に実行される Macro

  10. None
  11. Macro定義

  12. Macro呼出

  13. Macro呼出 Macro展開

  14. Macro展開後のコードがコンパイルされる

  15. どういった利点があるのか

  16. ✔ 重複を排除しやすい
 ✔ メタプロ風味 ✔ DSLを提供しやすい

  17. 重複を排除しやすい

  18. None
  19. 重複

  20. 重複

  21. 重複 重複排除

  22. getter だけなのに複雑では…

  23. getterあります Object クラスに定義されている Macro

  24. 便利 Macro たくさん! https://crystal-lang.org/api/Object.html

  25. Crystal Advent Calendar 2017 便利 Macro 紹介してます

  26. 重複を排除しやすい ✔ ある程度は重複排除できる
 ✔ 便利 Macro がすでにいくつかある

  27. メタプロ風味

  28. ✔ rubyだと instance_methods と define_method ? 各メソッドの実行時間を出力したい! お題

  29. 簡単なライブラリクラスを書いてみる 「MethodProf」クラスとでも名付けよう

  30. まずは使用例

  31. 出来上がりイメージ

  32. 出来上がりイメージ 1秒 sleep して String を返す

  33. 出来上がりイメージ 100万回 String を結合する

  34. 出来上がりイメージ 100万回 String を2つ結合している

  35. 出来上がりイメージ MethodProf を include して Macro を展開している

  36. 出来上がりイメージ インスタンスを生成してメソッド呼び出し

  37. 出来上がりイメージ

  38. MethodProf クラス

  39. None
  40. macro構文で定義(引数はio)

  41. ・@type は型情報にアクセスできる ・@type.methods で型に定義されているmethodの配列を得る  - ArrayLiteral(Crystal::Macros::Def) ・initializeメソッドは計測対象から外すので配列から除去 → initialize 以外の

    method 情報で for を回す
  42. ・展開される Crystal のコードを定義 ・呼出先に def 構文が展開される

  43. ・メソッド名は Crystal::Macros::Def#name で得る ・引数は Crystal::Macros::Def#args で得る  - 「*」をつけて splat展開 している

  44. ・計測のために時間を保持

  45. ・lambda を生成  - 引数は対象のメソッドと同じ  - メソッドの中身は Crystal::Macros::Def#body で得る ・lambda を即座に

    call する
  - 渡す引数は args の名前を羅列  - *(m.args) は型情報も含まれてしまうので、ここでは名前だけ展開 ・return_value に格納
  46. ・計測結果を io に出力

  47. ・lambda の結果を返す

  48. Macro を展開するとこうなります loop2を例に

  49. Macro定義 loop2展開後

  50. Macro定義 loop2展開後

  51. Macro定義 loop2展開後

  52. Macro定義 loop2展開後

  53. Macro定義 loop2展開後

  54. Macro定義 loop2展開後

  55. ✔ ASTNodeの情報からコンパイル時にコードを生成
 ✔ splat展開が優秀だった! ✔ もちろん「動的なclass定義」はできない メタプロ風味

  56. DSLを提供しやすい

  57. https://github.com/at-grandpa/clim

  58. https://github.com/at-grandpa/clim ✔ ruby の thor 風 syntax ✔ オプションの型指定
 ✔

    default / required
 ✔ サブコマンド
 ✔ カスタムヘルプ
  59. None
  60. None
  61. None
  62. すべて Macro ・最終的に全てクラスやメソッドの定義に展開される

  63. None
  64. コンパイル時に raise で落としてエラーメッセージを出す

  65. DSLに渡された文字列を使ってオプションの独自のクラスを定義 DSLに渡された文字列を使ってコマンドの独自のクラスを定義

  66. DSLを提供しやすい ✔ 複雑なコードも簡単に記述できるようになる

  67. ✔ 重複を排除しやすい
 ✔ メタプロ風味 ✔ DSLを提供しやすい Macro の利点おさらい

  68. Macro周りのtips

  69. デバッグしづらいのでは?

  70. エラー文が親切です!

  71. エラー文が親切です! Macro 展開後のどこでエラーが起きているかわかる

  72. エラー文が親切です! エラー内容も具体的

  73. `crystal tool expand` があります! ✔ ファイルとカーソル位置指定 ✔ カーソル位置の Macro を展開して表示

  74. crystal tool expand ファイルとカーソル位置を指定

  75. crystal tool expand 展開されたコードを見ることができる

  76. `macro` ディレクティブなしでもOK

  77. 実は直接書けます 環境変数でcrystalコードを切り替えたりできます

  78. 展開後は正しいのにエラーなんだけど

  79. ❌ Macro展開後、全体のコードが正しい ⭕ Macro展開のみで出来たコードが正しい

  80. ❌ Macro展開後、全体のコードが正しい ⭕ Macro展開のみで出来たコードが正しい

  81. ❌ Macro展開後、全体のコードが正しい ⭕ Macro展開のみで出来たコードが正しい この Macro 展開だけでは `when` だけしか展開されない
 →

    Macro展開だけのコードを見ると syntax error → コンパイラ落ちる
  82. begin ~ end を使う

  83. begin ~ end を使う begin ~ end で囲めば、 ひとつのMacroとして扱われる →

    展開後のコードは正しい ※ 実は {% begin %} は {% if true %} のシンタックスシュガーです
  84. まとめ

  85. ✔ 「Crystalのコード」を書くコード ✔ コンパイル前に実行される
 ✔ 重複を排除しやすい
 ✔ メタプロ風味 ✔ DSLを提供しやすい

    Macro とは
  86. Happy Crystalling fin