Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

@at_grandpa

Slide 3

Slide 3 text

圧倒亭グランパのブログ

Slide 4

Slide 4 text

Crystal Advent Calendar 2017

Slide 5

Slide 5 text

Crystal Advent Calendar 2017

Slide 6

Slide 6 text

Macroについて

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Macro定義

Slide 12

Slide 12 text

Macro呼出

Slide 13

Slide 13 text

Macro呼出 Macro展開

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

重複を排除しやすい

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

重複

Slide 20

Slide 20 text

重複

Slide 21

Slide 21 text

重複 重複排除

Slide 22

Slide 22 text

getter だけなのに複雑では…

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

メタプロ風味

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

まずは使用例

Slide 31

Slide 31 text

出来上がりイメージ

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

出来上がりイメージ

Slide 38

Slide 38 text

MethodProf クラス

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

macro構文で定義(引数はio)

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

・展開される Crystal のコードを定義 ・呼出先に def 構文が展開される

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

・lambda を生成  - 引数は対象のメソッドと同じ  - メソッドの中身は Crystal::Macros::Def#body で得る ・lambda を即座に call する
  - 渡す引数は args の名前を羅列  - *(m.args) は型情報も含まれてしまうので、ここでは名前だけ展開 ・return_value に格納

Slide 46

Slide 46 text

・計測結果を io に出力

Slide 47

Slide 47 text

・lambda の結果を返す

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Macro定義 loop2展開後

Slide 50

Slide 50 text

Macro定義 loop2展開後

Slide 51

Slide 51 text

Macro定義 loop2展開後

Slide 52

Slide 52 text

Macro定義 loop2展開後

Slide 53

Slide 53 text

Macro定義 loop2展開後

Slide 54

Slide 54 text

Macro定義 loop2展開後

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

DSLを提供しやすい

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

https://github.com/at-grandpa/clim ✔ ruby の thor 風 syntax ✔ オプションの型指定
 ✔ default / required
 ✔ サブコマンド
 ✔ カスタムヘルプ

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

すべて Macro ・最終的に全てクラスやメソッドの定義に展開される

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Macro周りのtips

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

エラー文が親切です!

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

❌ Macro展開後、全体のコードが正しい ⭕ Macro展開のみで出来たコードが正しい この Macro 展開だけでは `when` だけしか展開されない
 → Macro展開だけのコードを見ると syntax error → コンパイラ落ちる

Slide 82

Slide 82 text

begin ~ end を使う

Slide 83

Slide 83 text

begin ~ end を使う begin ~ end で囲めば、 ひとつのMacroとして扱われる → 展開後のコードは正しい ※ 実は {% begin %} は {% if true %} のシンタックスシュガーです

Slide 84

Slide 84 text

まとめ

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Happy Crystalling fin