『実践プログラミングDSL』
https://gyazo.com/8774b203aaa097517acad657f31e9bd3
2012/6/8
DSLの実装方法と、DSLの利用方法について
訳は自然でかなり読みやすい
構文のデザインとか
DDD的なドメインの話や、良い抽象化などの話が割と詳細に出てくる これは嬉しい誤算だmrsekut.icon
DSLを設計するというのはつまりこういうことなんだな
1章
DSLの概要
読了2020/5/3
2章
3章
4章
DSLのデザパタ
5章
動的片付きの言語での実装
6章
静的型付きの言語での実装
7章
8章
9章
付録A
付録B
メタプロ
付録C
ruby
付録D
scala
付録E
groovy
付録F
cloujure
付録G
多言語開発
第1部 DSL入門
第1章 ドメインの言葉を話す方法を学ぶ
1.1 問題ドメインと解決ドメイン
1.1.1 問題ドメイン
1.1.2 解決ドメイン
1.2 ドメインモデリング:共通語彙の準備
1.2.1 共通語彙の利点
グルー(接着剤)としての共通語彙
テストケースにおける共通のテクノロジー
開発期間中の共通語彙
1.3 DSL入門
1.3.1 DSLとは何か?
一般的に使われる言語とDSLはどう違うのか?
ビジネスユーザーにとってのDSLとは?
1.3.2 一般的なDSL
1.3.3 DSLの構造
1.4 DSLの実行モデル
1.5 DSLの分類
1.5.1 内部DSL
1.5.2 外部DSL
1.5.3 テキストベース以外のDSL
1.6 いつDSLを利用するか
1.6.1 DSLの長所
DSLは表現力豊かである
DSLは簡潔である
DSLは高い抽象化レベルで設計されている
DSLはより高いリターンをもたらす
DSLベースの開発はスケーラブルである
1.6.1 DSLの短所
言語の設計は難しい
DSLは上流工程でのコストとなる
DSLの利用はパフォーマンスに影響を与える
DSLではツールによるサポートが期待できない場合がある
「また新しい言語を覚えなくちゃ」症候群
DSLによって言語間の不協和音が発生しうる
1.7 DSLと抽象の設計
1.8 まとめ
1.9 参考文献
第2章 最初の一歩
2.1 Javaで最初のDSLを実装する
2.1.1 共通語彙の準備
2.1.2 Javaによる最初の実装
Orderクラスを実装する
Orderビルダーの利用
JavaによるDSLの分析
2.2 DSLをよりわかりやすくする
2.2.1 XMLを使ってドメインを外部化する
2.2.2 Groovy:より表現力豊かな実装言語
Groovyによるソリューション
methodMissingを使ったメソッドの組み込み
動的メソッド注入におけるGroovyのメタプログラミング技法
高階関数とクロージャに対する正規のサポート
2.2.3 Groovy DSLの実行
2.3 DSLの実装パターン
2.3.1 内部DSLパターン:共通特性と多様性
スマートAPI
構文木操作
型埋め込み
リフレクティブメタプログラミング
実行時メタプログラミング
コンパイル時メタプログラミング
2.3.2 外部DSLパターン:共通特性と多様性
コンテキスト駆動の文字列操作
XMLから利用可能なリソースへの変換
DSLワークベンチ
外部の埋め込みコードとDSLの混合
パーサーコンビネータベースのDSL設計
2.4 DSLの実装方法を選択する
既存のインフラストラクチャの再利用
既存の知識の活用
外部DSLの学習曲線
必要十分な表現力
合成可能性
2.5 まとめ
2.6 参考文献
第3章 DSLを使ったアプリケーション開発
3.1 DSLの組み込み方法
3.1.1 DSLの統合に注意すべき理由
3.2 内部DSLを組み込むためのパターン
3.2.1 Java 6のスクリプトエンジンの利用
Groovy DSLの準備
DSL実装とスクリプトの統合
Java 6のスクリプティングに関する問題
3.2.2 DSLラッパーの利用
準備作業
DSLの構築
Scalaの暗黙の型変換を使う
ユーザーが得るメリット
3.2.3 言語特有の統合機能
JavaコードとGroovy DSLの間のやり取り
Groovyクラスローダーを使ったより優れた統合方法
最終結果
3.2.4 Springベースの統合
Springの動的言語サポート
実装の構成
3.3 外部DSL向けの統合パターン
3.4 エラーと例外のハンドリング
3.4.1 例外の命名
3.4.2 入力エラーのハンドリング
型システムが使えない場合
パーサーの役割
3.4.3 例外的なビジネス状況の処理
3.5 パフォーマンスの管理
3.6 まとめ
3.7 参考文献
第2部 DSLの実装
第4章 内部DSLの実装パターン
4.1 DSLの道具箱を準備する
4.2 埋め込みDSL:メタプログラミングのパターン
4.2.1 暗黙的コンテキストとスマートAPI
DSLの表現力を判定する
暗黙的コンテキストの定義
スマートAPIを使って表現力を向上させる
4.2.2 動的なデコレーターによるリフレクティブメタプログラミング
Javaによるデコレーター
Javaでの実装を改善する
Rubyによる動的なデコレーター
4.2.3 ビルダーを使ったリフレクティブメタプログラミング
Groovyビルダーの魔法
Groovyビルダーの内部
4.2.4 教訓:メタプログラミングのパターン
4.3 埋め込みDSL:型付けされた抽象を使うパターン
4.3.1 汎用的な抽象としての高階関数
グループ化された報告書の生成
基礎となる抽象の準備
最初の試行:特化した実装
汎用的な実装
4.3.2 明示的に型付けした制約によるドメインロジックのモデル化
Rubyにおける実行時検証
Scalaにおける明示的に型付けされた制約
4.3.3 教訓:型を使って考える
4.4 生成型DSL:実行時に定型コードを生成する
4.4.1 生成型DSLの動作原理
4.4.2 簡潔なDSLの設計に使えるRubyのメタプログラミング
クラスメソッドを使って妥当性検査を抽象化する
動的なメソッド生成を行うミクシン
最後のグルー(接着剤)
4.5 生成型DSL:マクロによるコンパイル時のコード生成
4.5.1 Clojureによるメタプログラミング
4.5.2 ドメインモデルの実装
4.5.3 Clojureマクロの美しさ
4.6 まとめ
4.7 参考文献
第5章 Ruby、Groovy、Clojureによる内部DSLの設計
5.1 動的な型付けによってDSLを簡潔にする
5.1.1 読みやすさ
5.1.2 ダックタイピング
ダックタイピングによるポリモーフィズムの実現
取引ドメインの例
5.1.3 メタプログラミングについてもう一度
5.1.4 なぜRuby、Groovy、Clojureなのか
5.2 Rubyによる取引処理DSL
5.2.1 APIから始める
基礎となる抽象
DSLファサード
5.2.2 ちょっとしたモンキーパッチ
5.2.3 DSLインタプリタの実装
インタプリタの追加
ボブの言語を話すということ
5.2.4 デコレーターとしてドメインルールを追加する
取引DSL:今いる場所
ドメインルールの実装
デコレーターを使ったRuby DSL
5.3 注文処理DSL:Groovyの最後のフロンティア
5.3.1 これまでの注文処理DSL
5.3.2 メタプログラミングのスコープの制御
GroovyのMOPとカテゴリー
基本的なDSL
5.3.3 仕上げ
5.4 Clojureを使って考え方を変える
5.4.1 ドメインオブジェクトの構築
5.4.2 デコレーターを使ってドメインオブジェクトをエンリッチする
Clojureのコンビネータの使い方
高階関数を使ったデコレーター
Clojureのマクロを使ってまとめ上げる
これまでにやったこと
5.4.3 REPLによるDSLセッション
5.5 従うべき指針
5.5.1 複雑さ最小の原則の遵守
5.5.2 最適な表現力の尊重
5.5.3 入念に設計された抽象の原則を弱めない
5.5.4 言語間の不協和音を避ける
5.6 まとめ
5.7 参考文献
第6章 Scalaによる内部DSLの設計
6.1 なぜScalaか?
6.2 Scala DSLに向けた最初の一歩
6.2.1 Scala DSLを使ったJavaオブジェクトのテスト
6.2.2 JavaオブジェクトのラッパーとしてのScala DSL
6.2.3 さほど重要ではない機能をScala DSLでモデル化
6.3 ScalaでDSLを使ってみよう
6.3.1 表現力豊かな構文
6.3.2 ドメイン抽象の生成
証券
口座と取引
6.4 取引生成DSLの構築
6.4.1 実装の詳細
暗黙の型変換
暗黙の型変換の詰め合わせ
implicitとレキシカルスコープ
6.4.2 DSL実装パターンのバリエーション
6.5 DSLでビジネスルールをモデル化する
6.5.1 拡張可能なVisitorとしてのパターンマッチング
6.5.2 ドメインモデルの強化
6.5.3 DSLによる税および手数料計算のビジネスルール
適用可能な税および手数料のリストを取得する
税および手数料の算出
DSLとAPI:何が違うのか?
6.6 すべてをつなぎ合わせる
6.6.1 トレイトと型を使ったさらなる抽象
6.6.2 具象ドメインコンポーネントの作成
6.7 DSLの合成
6.7.1 拡張を使った合成
DSLを成長させる
プラグイン可能なセマンティクスを持った合成DSL
関数コンビネータを使った合成
完全に合成されたDSLを使う
6.7.2 階層的な組み合わせを使って異なるDSLを合成する
実装の結合を避ける
残高をどのようにモデル化するか
残高DSLをポートフォリオDSLに合成する
6.8 DSLのモナド構造
モナドとは何か
モナドによる「偶有的な複雑性」の削減
モナドを使った取引DSLの設計
6.9 まとめ
6.10 参考文献
第7章 外部DSLの実装
7.1 外部DSLの解剖学
7.1.1 単純さが最優先
7.1.2 ドメインモデルの抽象化
大きなボックスをモジュラー化する
セマンティックモデル
セマンティックモデルの生成
7.2 外部DSL設計におけるパーサーの役割
7.2.1 パーサーとパーサージェネレータ
7.2.2 構文主導型変換
ANTLRを使った例をセットアップする
レクサーの設計
文法規則の設計
独自のアクションとして外部コードを埋め込む
パーサーモジュールの構築
ここまでの道のり
7.3 パーサーの分類
7.3.1 単純なトップダウンパーサー
LL(1)再帰下降パーサー
LL(k)再帰下降パーサー
7.3.2 より高度なトップダウンパーサー
再帰下降バックトラックパーサー
メモ化パーサー
述語制御パーサー
7.3.3 ボトムアップパーサー
演算子順位パーサー
LR(k)パーサー
LRパーサーのバリエーション
実際には何を使えばいいのか
7.4 Xtextを使ったツールベースのDSL開発
7.4.1 文法規則とアウトラインビュー
7.4.2 文法に対するメタモデル
7.4.3 セマンティックモデルに対するコード生成
Xpandテンプレートを使ったコード生成
DSLスクリプトの処理
長所と短所(ただし、長所が大部分)
7.5 まとめ
7.6 参考文献
第8章 Scalaのパーサーコンビネータを使った外部DSLの設計
8.1 パーサーコンビネータ
8.1.1 パーサーコンビネータとは何か
8.1.2 パーサーコンビネータによるDSLの設計
8.2 Scalaのパーサーコンビネータライブラリ
8.2.1 パーサーコンビネータライブラリ中の基本的な抽象
8.2.2 コンビネータによってパーサーをつなぎ合わせる
すべての文法規則は関数
逐次合成コンビネータ
選択コンビネータ
逐次合成(選択保持)コンビネータ
繰り返しコンビネータ
すべてをまとめる
8.2.3 DSLパーサーコンビネータ用のモナド
逐次合成コンビネータの実装:大変な方法
モナドの利用:ママ見て、余計なコードがなくなったよ!
8.2.4 Packratパーサーで左再帰のDSL構文をパースする
Packratパーサーはメモ化によって効率を上げている
Packratパーサーは左再帰をサポートする
Packratパーサーはスキャナなしでパースできる
意味述語のサポート
順序選択
8.3 パーサーコンビネータを使ったDSLの設計
8.3.1 ステップ1:文法の実行
8.3.2 ステップ2:DSLのセマンティックモデルの構築
関数適用に対するコンビネータ
部分関数の適用に対するコンビネータ
8.3.3 ステップ3:注文を表現する抽象の設計
8.3.4 ステップ4:関数適用コンビネータを使ってASTを生成する
8.4 Packratパーサーを使ったDSL
8.4.1 ドメインの問題の紹介
ビジネス処理の理解
実装するSSIルールのサンプル
8.4.2 文法の構築
8.4.3 セマンティックモデルの設計
8.4.4 パーサー合成によるDSLセマンティクスの拡張
独自の拡張に対するモナド
デコレーターとして独自のパーサーを設計する
デコレーターを追加する
8.5 まとめ
8.6 参考文献
第3部 DSL開発の将来
第9章 DSL設計:未来の展望
9.1 DSLの設計に対する言語サポートの拡充
9.1.1 表現力を向上させる努力
9.1.2 メタプログラミングの強力さ
9.1.3 XMLの代わりにS式を使う
9.1.4 パーサーコンビネータの普及
9.2 DSLワークベンチ
9.2.1 DSLワークベンチとは何か
9.2.2 DSLワークベンチを利用することによるメリット
9.3 IDEツールによるサポート
9.4 DSLの進化
9.4.1 DSLのバージョニング
9.4.2 DSLのスムーズな進化に関するベストプラクティス
暗黙的コンテキストを使ってバージョンの進化を容易にする
自動変形による後方互換性の維持
DSLファサードで多数のバージョン管理問題に対処する
入念に設計された抽象の原則に従う
9.5 まとめ
9.6 参考文献
付録A ドメインモデリングにおける抽象の役割
A.1 入念に設計された抽象の特性
A.1.1 最小化
A.1.2 蒸留
A.1.3 拡張性と合成可能性
A.2 最小化:約束したものだけを公開する
A.2.1 汎用的な型を使った進化
A.2.2 実装の露出を防ぐためのサブタイプ化
A.2.3 実装継承を正しく使う
A.3 蒸留:必要なもののみを残す
A.3.1 何が“余計な”ものなのか
A.3.2 偶有的な複雑性
A.3.3 汚染の除去
A.3.4 DIによって実装の詳細を追い出す
A.4 拡張性による段階的な成長の実現
A.4.1 拡張性とは何か
A.4.2 ミクシン:拡張性のためのデザインパターン
A.4.3 Mapの拡張にミクシンを使う
A.4.4 関数拡張性
A.4.5 拡張性も「猿真似」になり得る
A.5 合成可能性は純粋性から生まれる
A.5.1 合成可能性に対するデザインパターン
A.5.2 言語に戻って
プロトタイプベースのオブジェクト指向
どこにでもあるメタプログラミング
A.5.3 副作用と合成可能性
コマンドとクエリーの分離
Haskellでの例
A.5.4 合成可能性と並行性
A.6 参考文献
付録B メタプログラミングとDSLの設計
B.1 DSLにおけるメタプログラミング
B.1.1 DSL実装における実行時メタプログラミング
B.1.2 DSL実装におけるコンパイル時メタプログラミング
C++:テンプレート
LispとClojure:マクロ
Java:アノテーション処理とAOPのサポート
B.2 DSLとしてのLisp
B.2.1 Lispの何が特別なのか
B.2.2 データとしてのプログラムコード
B.2.3 プログラムコードとしてのデータ
B.2.4 リスト構造だけをパースする単純なパーサー
B.3 参考文献
付録C RubyのDSL機能リファレンス
C.1 Rubyが持つDSL向けの機能
C.2 参考文献
付録D ScalaのDSL機能リファレンス
D.1 Scalaが持つDSL向けの機能
D.2 参考文献
付録E GroovyのDSL機能リファレンス
E.1 Groovyが持つDSL向けの機能
E.2 参考文献
付録F ClojureのDSL機能リファレンス
F.1 Clojureが持つDSL向けの機能
F.2 参考文献
付録G 多言語開発
G.1 IDEにどんな機能を求めるか
G.2 Java/Groovy開発環境の構築
G.3 Java/Scala開発環境の構築
G.4 多言語開発向けの人気の高いIDE
監訳を終えて — 佐藤 竜一
索引
コラム目次
金融取引システムの基礎知識
金融取引システム:取引と決済
金融取引システム:顧客注文の処理
金融取引システム:顧客の口座
金融取引システム:取引の現金価格
金融取引システム:証券の種類