日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

Fluent API におけるプログラム断片のより柔軟な結合
中道 晃平

中丸 智貴

森畑 明昌

Fluent API はメソッド呼び出しの連鎖によって埋め込み DSL を実装するデザインパターンの一つで，ホスト言語の型検査を利用してゲスト言語の構文検査をする研究がなされている．本論文では DSL のトークンをメソッドの連鎖で繋ぐのではなく，演算子を利用して繋ぐ ﬂuent API のデザインを提案する．演算子で繋ぐデザインによって，DSL の断片のより簡潔な構築と結合が可能になる．しかし，既存のプログラム断片の型付けを行う ﬂuent API 設計
手法では，プログラマにとって自然なプログラムが書けないことがある．そこで本論文では既存手法の１つである，
DSL の文法を変形することによりプログラム断片の型付けを可能にする手法（Yamazaki ら，SLE2022）を拡張することより，より柔軟なプログラム断片の組み合わせを可能にする Fluent API の設計が可能であることを示す．

1 はじめにFluent API は，ゲスト言語のプログラムをメソッドの連鎖によってホスト言語中に記述するという，
埋め込み DSL のデザインパターンである．図 1 はFluent API によって C++に下記の SQL プログラム

Query query = Query.select().column("*").from().table("BOOK").where().column("PRICE").le().num(2000);
query.run();
図 1: Fluent API を利用した SQL 埋め込みの例

を埋め込んだ例である．

SELECT *リとしては，JOOQ†1 や SuperTest†2 などが挙げら

FROM BOOKれる．

WHERE PRICE <= 2000

Fluent API の欠点としては，なんの工夫もしな

この例では，SQL のキーワード名を名前とするメソッ

かった場合，ゲスト言語のプログラムとして全く意味

ドをドット演算子を用いて連鎖的に呼び出している．

をなさないメソッド連鎖が容易に記述できてしまうとドット演算子やメソッド呼び出しの括弧等を無視すれいう点が挙げられる．例えば，図 2 のコード例では，

ば，これは SQL プログラムをそのまま書いているか

select が連続して出現しているため，SQL の構文にのような記述となっている．

は合致しない．このような構文的な誤りを静的に発見

Fluent API には，ホスト言語においてメソッドの連鎖が可能であることだけが求められる．そのため，

できれば，プログラムの実行前にプログラマがバグに気づくことができ，開発効率が向上する．

広い範囲のホスト言語・ゲスト言語に対して利用可能

そこで近年，ﬂuent API で記述された埋め込み DSLである．実際に ﬂuent API を利用しているライブラのプログラムやその断片が，ゲスト言語の構文規則に合致しているかどうかを，型検査を利用してチェッFlexible concatenation of program fragments in ﬂuent
API
Kohei Nakamichi, Tomoki Nakamaru, Akimasa Morihata, 東京大学大学院総合文化研究科, Graduate Schoolof Arts and Sciences, The University of Tokyo.

クする研究がなされている [4] [7] [9] [10] [11]．例えば，

Gil らの手法 [4] では決定性文脈自由言語を対象にし†1 https://www.jooq.org/†2 https://github.com/ladjs/supertest

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

2
Query query = Query

From origin = from() + table("BOOK").select().column("*").select().column("*").from().table("BOOK")
query.run();ColumnList cols1 =図 2: SQL の構文的に正しくない記述の例

From origin = from() + table("BOOK")

column("PRICE") + column("PRODUCT_ID");
ColumnList cols2 =column("QUANTITY") + column("NAME");
ColumnList col3 = cols1 + cols2;
Query query = select() + cols3 + origin;

図 4: 既存手法では構文検査に失敗する例

Where cond =where() + column("PRICE") + le() + numている [7] [11]．しかし，これら既存手法においては，(2000);
Query query =select() + column("*") + origin + cond;

どのようなプログラム断片の組み合わせが結合可能

図 3: 提案手法による SQL 埋め込みの例てはプログラム断片の結合をするプログラムが書けであるかは，DSL の仕様として与えられる文脈自由文法に強く制限される．そのため，プログラマにとっないことがある．
て，決定性プッシュダウンオートマトンを型システム

一例を図 4 に示す．このプログラムでは，抽出したを用いてシミュレートすることで構文検査を実現して

い列名を col1 と col2 という 2 つの部分に分けて構いる．型システムによる構文検査では，大量の型やメ成し、それらを結合して col3 を構成している。一見

ソッドの定義が必要になる．そのため，これらの研究

自然だが，既存手法に基づいて構文検査をしようとすの多くは，文法を入力として構文検査のためのプログ

ると，構文エラーと判定されてしまう．

ラムを出力する ﬂuent API 生成器の作成を目指している．

本研究では，上記の問題が既存研究 [10] の自然な拡張で解決できることを示す．この手法は，Fluent API

本研究では，メソッド連鎖を用いて DSL のトーク生成器へ入力する文法を事前に変形しておくものでンを 1 つずつで繋ぐのではなく，演算子を利用してある．そのため，型システムによる構文検査に際してプログラム断片同士を繋ぐ ﬂuent API のデザインをは，既存手法をそのまま流用することが可能である．

提案する．例として、図 3 に，提案手法を用いて図 1

本研究の主な貢献は次の通りである．

のプログラムを記述したものを示す．プログラム断片• 演算子オーバーロードを利用した，演算子で埋は+演算子を用いて結合される．
この方式では，メソッド連鎖を利用する既存方式にめ込み DSL のトークンを繋ぐデザインの ﬂuent

API を提案する．

比べ，プログラムを意味上にまとまった部分ごとに構• プログラマにとって直感的な記述であるにもか築し，それらを結合して大きなプログラムを構成してかわらず，既存手法では扱うことのできないプロ

ゆくようなプログラミングが，より自然に表現でき

グラム断片の結合が存在することを示す．

る．例えば図 3 のプログラムであれば，データを取り出すテーブルや取り出されるレコードを特定する• 既存研究の上記の問題点が，文法を適切に変形することによって解消できることを示す．

条件が，クエリ全体とは独立したプログラム断片として定義できている．そのため，ﬂuent API を用いた

2 背景プログラム記述におけるモジュール性を改善すると期

2. 1 Fluent API における構文検査

待できる．

Fluent API における構文検査では，メソッドを

Fluent API において，プログラム断片を組み合わ

トークンとみなす．正しいメソッドの連鎖であるとせることをある程度許す方式についても研究がなされは，メソッドの連鎖をトークン列とみなしたときにそ日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

3のトークン列がゲスト言語の文法仕様を満たしているということである．
構文検査手法の例として，Yamazaki らの手法 [11]の概要について説明する．C++の型システムを利用して LR オートマトン [3] のシミュレートをする．

LR オートマトンの状態は型によってエンコードされる．LR オートマトンの状態におけるスタックは，

S1<S2<Bottom>>のようにテンプレート引数を入れ子にすることで表現できる．
オートマトンの状態遷移はメソッド呼び出しによっ

IncompleteChain tmp = Query.select().column("*").from().table("BOOK");
Query query;
if(b) {
query =
tmp.where().column("PRICE").le().num(2000);} else {query =
tmp.where().column("PRICE").ge().num(2000);}て行われる．メソッド連鎖の開始は，オートマトンの開始状態に対応する型をもつオブジェクトである．オ

図 5: Flat-chaining style のコード例

ブジェクトからのメソッド呼び出しにより，オートマ
トンの遷移が引き起こされる．遷移後の状態は，メソッドの返り値の型として返される．メソッド呼び出しの連鎖によりオートマトンが状態遷移していくことで，トークン列が文法仕様を満たしているかが検査される．トークン列が文法仕様を満たしていないとき
は，メソッド呼び出しは型検査に失敗する．

2. 2 Sub-chainining style
Fluent API に対する静的な構文検査の研究の多
くは，切れ目や入れ子のない，ひと繋がりのメソッ
ド連鎖を対象にしている．このような ﬂuent API は

Where cond;if(b) {
cond = Where.where().column("PRICE").le().num(2000);} else {cond = Where.where().column("PRICE").ge().num(2000);}
Query query = Query.select().column("*").from().table("BOOK").where(cond);

ﬂat-chainining style と呼ばれる，
ゲスト言語のプログラム断片を作成し，断片同士を図 6: Sub-chaining style のコード例

組み合わせることのできるデザインの ﬂuent API についても研究がなされている [7] [11]．このような方式

ム全体を先頭から順に作成する必要があるのに対は，ﬂat-chainining style に対して，sub-chainining

し，sub-chainining style ではプログラムを一部分ず

style と呼ばれる，

つ作って組み合わせる記述が可能になる．そのため，

図 5 と図 6 は同じ意味のコードをそれぞれ ﬂat-chainining style と sub-chainining style で記述した

sub-chainining style のほうがモジュール性に優れるといえる．

ものである．どちらも，SQL クエリの where 節が条件
変数 b の値によって変化する．Flat-chainining style
では，不完全なメソッド連鎖を一旦変数 tmp に束縛

2. 3 Yamazaki らによる sub-chaining styleの実現手法

し，if 文の中でこれに where 節に対応するメソッド

Sub-chainining style を実現する手法として，本研

連鎖を付け加える．一方で sub-chainining style では，

究と関わりの深い Yamazaki らの手法 [10] を紹介する．

where 節に対応するメソッド連鎖を一度変数 cond にYamazaki らの手法では，まず文法仕様として与え束縛して，それを用いてクエリ文全体を組み上げる．

られた文法の変形を行う．文法の変形においては，各

このように，ﬂat-chainining style ではのプログラ

非終端記号に対してその非終端記号から導出される

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集4

ようなメソッド列を受け取るためのメソッドを追加す

ムは cond を直接演算子で繋いでいる．

る．図 7 を変形したものを図 8 にを示す．赤字が追演算子で繋ぐスタイルの ﬂuent API ではメソッド加されたメソッドを示している．Select(Select) は連鎖で繋ぐスタイルの ﬂuent API と比較して利点がSelect から導出されるメソッド列の断片を受け取る，

2 つある．

Select という名前のメソッドを表している．

1 つめは演算子を利用することで，埋め込み DSL

そして，メソッド列の断片が各非終端記号から導に括弧を可読性の高い形で導入することが可能になる

出されるすることができるかをチェックするために，ことである．メソッド連鎖を利用する ﬂuent API に

各非終端記号を開始記号として既存の ﬂat-chaining

括弧を導入した場合が図 11 である．図 9 と同じクエ

style の手法で構文検査を行えるようにする．メソッ

リを記述しているが，括弧として begin, end を利用

ド列がある非終端記号から導出可能なときは，開始記している．多くのプログラミング言語ではメソッド名号となる非終端記号を指定して構文検査をおこなう

として () などの記号を利用できない．そのため，メことができる．構文検査が行われたトークン列には，

ソッド連鎖を利用する ﬂuent API では括弧を表現す

開始記号とした非終端記号に対応する型がつく．これるためにアルファベットの列を利用する必要がある．

によって，文法変形の際に追加したメソッドが受け取

しかし，アルファベットの列で表現された括弧はひとるメソッド列が，対応する非終端記号から導出される目で判別することが困難である．括弧はプログラミン

かをチェックすることができる．

グ言語の構文において曖昧性の除去などの重要な役割を果たす．括弧の視認性が高いことは大きな利点と3 提案：演算子オーバーロードを利用した
ﬂuent APIなる．

2 つめは，演算子で繋ぐスタイルではトークン列の本研究では埋め込み DSL のトークンをメソッド連

部分列を結合する記述がシンプルになるとことであ鎖で繋ぐのではなく，演算子で繋ぐスタイルの ﬂuent

る．図 6 のプログラムより，図 10 のプログラムのほ

API を提案する．

うがトークン列の結合はシンプルであった．演算子で図 9 は演算子で繋ぐスタイルの ﬂuent API のプロ

繋ぐスタイルでは，ゲスト言語のトークンがホスト言

グラム例である．既存手法ではメソッド連鎖で DSL

語において第一級市民であるためにシンプルに結合のトークン結合していたところを，二項演算子+にすることが可能となっている．トークン列を繋ぎ合わ

よって結合している．さらに，単項演算子~も利用しせて全体を構成していくという観点で，演算子を用いている．ゲスト言語において括弧で囲む必要のある部

る方がメソッド連鎖を用いるより直感的な記述が可能

分を，ホスト言語の括弧で囲み，単項演算子~を適用となる．

している．単項演算子~が必要なのは，括弧を付ける部分がホスト言語の式において先頭の場合にホスト

4 問題点とその解決

言語側で括弧がついているかどうか識別が困難にな

Sub-chaining style を用いることで埋め込み DSL

るからである．

のトークン列を結合させて記述するプログラムを自然演算子で繋ぐスタイルの ﬂuent API ではトークンに書くことができる．しかし，既存の sub-chaining

列の断片を変数で束縛したり，関数で返したりする記

style を実現する手法では，文法の曖昧性が問題とな述も可能である．図 10 のプログラムは図 6 のプログ

り，プログラマにとって自然な記述ができないことがラムを，演算子で繋ぐデザインで書き換えたものであある．本節ではそのようなプログラム例を示すと同時る．トークン列の断片が束縛された変数である condに，その解決策を示す．

を結合する，最後の行の記述が特に異なっている．図6 のプログラムではトークン列の断片を受け取る専用のメソッドを用いる必要があった．図 10 のプログラ

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

5

Query ::= Select From Where
Select ::= select() column(string)
From ::= from() table(string)
Where ::= where() column(string) Op num(int)
Op ::= le() | ge() | eq()
図 7: SQL の文法例

Query ::= Select From Where
Select ::= select() column(string)| Select(Select)
From ::= from() table(string)| From(From)
Where ::= where() column(string) Op num(int)| where(Where)
Op ::= le() | ge() | eq()
| Op(Op)

Where cond;if(b) {
cond = where() + column("PRICE") + le() +num(2000);} else {cond = where() + column("PRICE") + ge() +num(2000);}
Query query = select() + column("*") + from()+ table("BOOK") + cond;

図 8: 文法変形の例．赤字が変形により追加された終図 10: 演算子を用いてトークン列の断片を組み合わ

端記号である．

せるプログラム例

Query query =Query query =select() + column("*")+ from() + table("BOOK")+ where() + column("PRICE") + eq() + ~(select() + max("PRICE")+ from() + table("BOOK"));
図 9: 演算子を用いた ﬂuent API の記述例．サブクエ

select().column("*").from().table("BOOK").where().column("PRICE").eq().begin().select().max("PRICE").from().table("BOOK").end();

図 11: メソッド連鎖を用いたサブクエリの記述例

リの記述をおこなっている．
クン列が，非終端記号の列 Select From から導出さ

4. 1 非終端記号の列から導出可能なトークン列
4. 1. 1 動機となる例

れることが保証されている．
これを既存手法で実現するには，図 7 の文法に非既存の sub-chaining style を実現する手法 [7] [10] で終端記号 SelectFrom を追加し，図 13 のような文法は，１つの非終端記号から導出可能なトークン列にしにすることが考えられる．この文法では，非終端記号

か型検査を行うことができない．しかし，複数の非終SelectFrom によって非終端記号の列 Select From か端記号から導出されるようなトークン列を扱いたい

ら導出されるトークン列を表現できる．

場合がある．

複数の非終端記号から導出されるようなトークン

図 12 は SQL の select 節と from 節を同じ変数に束列を扱う例としては図 14 のようなプログラムも考え縛している例である．変数 target に束縛されるトーられる．このプログラムでは変数 rows に from 節と日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

6
SelectFrom target;

FromWhere rows;

if(b) {

if(b) {

target = select() + column("QUANTITY")

rows = from() + table("ORDER")+ from() + table("ORDER");} else {target = select() + column("STOCK")+ from() + table("BOOK");+ where() + column("QUANTITY") + le()+ num(10);} else {rows = from() + table("ORDER")+ where() + column("STOCK") + le() +num(10);}
Query q = target + where() +column("PRODUCT_ID") + eq() + num(42);

図 12: Select 節と From 節を同じ変数に束縛するプ}
Query q = select() + column("PRODUCT_ID") +
rows;

ログラム
図 14: From 節と Where 節を同じ変数に束縛するプ
ログラム

Query ::= SelectFrom Where
SelectFrom ::= Select From
Select ::= select() column(string)

4. 1. 2 解決法
前節では 2 つ以上の非終端記号から導出可能なトー

From ::= from() table(string)

クン列を，複数種類扱うことを考えた．しかし，これWhere ::= where() column(string)をナイーブに行おうとすると文法が曖昧になってしま

Op num(int)

うことがあった．

Op ::= le() | ge() | eq()

この問題は 2. 3 節で紹介した Yamazaki らの手法

図 13: 非終端記号 SelectFrom を追加した文法の例

を拡張することで解決できる．Yamazaki らの手法における文法の変形をするときに，図 8 の代わりに図where 節を束縛している．

15 のように変形する．ハット（ b ）がついた記号は，

このプログラムを既存手法で実現するため

その型がついたトークン列の断片を表す終端記号でに，SelectFrom の場合と同じように，非終端記号\ は Select から導出可能なトーある．例えば，Select

FromWhere を文法に追加することが考えられる．
しかし，このアプローチでは非終端記号 SelectFrom

クン列や，そのようなトークン列が束縛された変数を表している．†3と FromWhere を共存させた場合に文法が曖昧になっ

図 15 では図 13 と異なり，Query から SelectFromてしまうという問題が発生する．なぜなら，非終端記は導出されない．その代わりに SelectFrom に対応す

号 Query から Select From Where を導出する過程と\る終端記号である SelectFromを導出できる．Select-

して，From から導出されるトークン列の断片を結合するとQuery −→SelectFrom Where −→Select From Where

きに，文法上では非終端記号 SelectFrom を介さずにQuery −→Select FromWhere −→Select From Where

結合可能となる．この変形によって，Query から導の２つが考えられるからだ．

出される記号列が自由度上がりすぎないようになって

Fluent API の構文検査を実現する既存手法では曖
昧な文法を扱うことはできない．そのため上記の例のような複数の非終端記号から導出されるようなトークン列を扱いたい場合では既存手法が不十分である．

いる．
一般に A −→ X1 . . . Xn という形の文法規則があ\ は，図 8 における Select(Select) に対応す
†3 Select
る．トークンの結合に演算子オーバーロードを用いるのに合わせて記法を変更している．

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集7

図 17 は図 16 の文法をもとにした複数のカラムをQuery ::= Select From Where\| SelectFrom
Where\| Select FromWhere

並べるプログラムである．1 行目と 2 行目は，それぞれ変数 columns1 と columns2 にカラムを並べたものを束縛してる．3 行目は columns1 と columns2

SelectFrom ::= Select Fromを連結して columns3 に束縛している．columns3 はFromWhere ::= From Where

ColumnList の型がつけられている．これには次のよ

Select ::= select() column(string)\| Select
From ::= from() table(string)[| Fromうな意図がある．

ColumnList は column(string) が 0 個以上連結したものを表している．したがって，ColumnList から導出される columns1 と columns2 を連結しても，Where ::= where() column(string)column(string) が 0 個以上連結したものとなるはずOp num(int)である．そのため，columns3 の型は ColumnList と\| WhereOp ::= le() | ge() | eq()c|Opなっている．
し か し ，図 16 の 文 法 で は ColumnList か ら

ColumnList ColumnList を導出することができな

図 15: SelectFrom と FromWhere を共存させる文法．

いので，3 行目のプログラムは構文検査に失敗してし

図 8 との相違点を赤い文字で示している

まう．

り，Xi . . . Xj (i ≤ j) から導出されるトークン列の構の 手 法 で は ，図 17 の 最 後 の 行 の 構 文 検 査 は 実 現

文検査を行いたいとする．このときすることはできない．図 17 の最後の行を実現する

図 16 の文法をうまく変更しても，Yamazaki ら• 非終端記号には，ColumnList から ColumnList ColumnList が– Y

導出できなければならない．しかし，そのような• 終端記号

ことのできる文法は曖昧である．ColumnList からY– b

ColumnList ColumnList ColumnList を導出する列• 生成規則– Y −→ Xi . . . Xj
– A −→ X1 . . . Xi−1 b
Y Xj+1 . . . Xnを追加することで文法が曖昧になることを回避することができる．変形前の文法から生成される言語は，変形後の文法で生成される言語の部分集合となっている．また，変形前の文法が曖昧でないなら変形後の文法も曖昧でない．

4. 2 繰り返しを含む文法
4. 2. 1 動機となる例
図 7 の文法を拡張した文法である図 16 の文法を考える．図 7 の文法では select 節に行を１つしか記述することができなかったが，図 16 では非終端記号

ColumnList によっていくつか並べたものを記述することができる．

として，
ColumnList−→∗ ColumnList ColumnList−→∗ ColumnList ColumnList ColumnList(1)

ColumnList−→∗ ColumnList ColumnList−→∗ ColumnList ColumnList ColumnListの２種類が考えられてしまうからである．ただし，式(1) において下線部は直後で導出により置き換えられる場所を示している．

4. 2. 2 解決法
図 17 の最終行の構文検査は，4. 1. 2 節で示した解決策と同様の手法で可能になる．Yamazaki らの手法における新たな非終端記号を追加するときに文法の変形を工夫する．
文法の変形としては図 18 のようにおこなう．ϵ日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

8

Query ::= Select From Where
Select ::= select() ColumnList
ColumnList ::= column(string)∗
From ::= from() table(string)
Where ::= where() column(string) Op num(int)
Op ::= le() | ge() | eq()
図 16: 非終端記号 ColumnList を追加した文法の例

ColumnList columns1 = column("PRICE") + column(

Query ::= Select From Where"PRODUCT_ID");

Select ::= select() ColumnList

ColumnList columns2 = column("QUANTITY") +\| Selectcolumn("NAME");
ColumnList columns3 = columns1 + columns2;

ColumnList ::= column(string) ColumnList\| ColumnList
ColumnList

図 17: ColumnList で型付けされたトークン列の断片|ϵ同士を結合するプログラム例

From ::= from() table(string)[| Fromは空列を表している．この文法では ColumnList は\column(string) と ColumnList
がいくつか並んだもWhere ::= where() column(string)\のとなっている．ColumnList
は ColumnList から導Op num(int)\| Where出されると型付けされたトークン列である．そのた

Op ::= le() | ge() | eq()

め，図 17 のように ColumnList と型付けされた変数c|Opを複数ならべたものも ColumnList から導出される

図 18: 図 17 のプログラムの構文検査を可能にする文と判定できるようになる．
図 18 の 文 法 で は ColumnList の 代 わ り に\ColumnList
を用いることで，導出 ColumnList −→法∗

ColumnList ColumnList が生じない．そのため，文の実装を行っている．ターゲット言語としては，演算法が曖昧になることを避けることができている．

子オーバーロードや高度な型の操作が可能である言

同 様 の 変 形 は A −→ X ∗ の 形 の 文 法 が あった∗語である C++を採用した．

ときに適用可能である．A −→ X の生成規則を

図 19 は ﬂuent API 生成器への入力例である．文

A −→ X A | b
A A | ϵ と変形する．この変形によって，

脈自由文法を記述して入力を行う．非終端記号は大文A から導出されるトークン列同士を結合させたとき

字始まりの変数名，終端記号は小文字始まりの名前にに A から導出されたものとして型が付く．変形前のなっている．終端記号は引数を取ることができ，()

文法が曖昧でなく，かつ A から導出されるものが X ∗の中に型を記述する．

のみであるなら，変形後の文法も曖昧でない．

入力された文脈自由文法に対して，まず文法の変形を行う．2. 3 節で示した Yamazaki らの手法と同じ変5 実装形に加えて，本研究特有の変形として X −→ A∗ の本研究では，3 章と 4 章で提案した演算子でトーク

形の文法があったときに，4. 2 節で示した文法の変形

ンをつなぐ ﬂuent API および，結合を柔軟にするたを行う．4. 1 節で示した文法変形については未実装でめの文法変形の一部を利用できる ﬂuent API 生成器

あり，実装することは今後の目標である．

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集
start: Query;9

API 生成器の作成を行っている．Gil と Roth [4] は，Query => Select From Where;

決定性文脈自由文法の文法仕様に対する構文検査手法

Select => select() ColumnList;

を Java の型システム上で実現する手法を示した．こColumnList => column(string)*;の手法では，決定性プッシュダウンオートマトンを型

From => from() table(string);

でシミュレートする．決定性文脈自由言語は LR(k)Where => where() column(string) Op num(int);

文法と等価であるが，この研究で作成された ﬂuent

Op => le() | ge() | eq();

図 19: Fluent API 生成器への入力例

API 生成器は LL(1) 文法のみを対象としている．この 2 つの研究では ﬂat-chaining style のみを対象としている．

文法を変形したあとに構文検査のために必要な型やRoth ら [9] は，決定性文脈自由言語の文法仕様に対関数定義の生成を行う．構文検査については C++をして Standard ML の型システムを用いて構文検査を用いた先行研究 [11] と同様に型システムを用いて LR行う手法を示している．この研究では sub-chainining

オートマトンをシミュレートして構文検査を行う．

style を扱う手法も示しているが，トークン列の断片

今回実装したシステムにおいて，構文検査が行わを直接構文検査するものではない．

れるタイミングは，多くの先行研究とは異なる．先トークン列の断片の構文検査を対象にした sub-

行研究ではメソッド呼び出しが行われるたびに，型

chainining style の 研 究 は 次 の よ う な も の が あ る ．

検査によってその呼出しが構文仕様に沿った呼び出し

Nakamaru ら [7] は文脈自由文法の一部について sub-かが検査される．一方で今回実装したシステムでは，

chainining style を実現する手法を示し，ﬂuent API

演算子で結ばれた時点ではその構文的な正しさは判

生成器の実装を行った．Yamazaki ら [10] 文法のこの定されない．演算子による返り値の型としては，トー研究では，ﬂuent API 生成器の実装までは行っていクン列を構成しているトークンがエンコードされたない．

もの返す．例えば，select() + column("PRICE")

これまでの ﬂuent API に対する構文検査の研究での 返 り 値 の 型 は ，TokenList<select terminal,

は，いずれも曖昧な文法を扱っていない．本研究ではcolumn terminal>のような型となる．構文検査が曖昧な文法を扱っている．

行われるのは明示的にコンストラクタが呼び出され構文解析において，曖昧な文法を扱う研究は多く

るか，トークン列が変数に束縛されるなど暗黙の型変なされている．パーサージェネレーターである Yacc

換が行われるタイミングである．例えば，図 9 のプ

[5] では演算子に優先順位をつけることによって曖昧ログラムでは変数 query にトークン列が束縛された性の回避を行っている．構文解析の際に，演算子の優ときに発生する型 Query への暗黙の型変換によって先順位に応じて複数ある構文木の候補から適切なもの

構文検査が行われる．構文検査では，トークン列の型を選択する [2]．Aasa らは文法に演算子の優先順位をの情報からそのトークン列が変換先の非終端記号か導入することが，文法の変形のみによって可能である

ら導出可能であるかを判定する．

ことを示している [1]．近年では，曖昧な文が与えられたときにユーザーに曖昧でなくなるようにプログ

6 関連研究ラムを書き直させるアプローチが提案されている [8]．

Fluent API は Fowler によって ﬂuent interface として提案され [6]，その構文検査については多くの研

参 考 文 献究が存在する．

[ 1 ] Aasa, A.: Precedences in speciﬁcations and implementations of programming languages, Theoretical Computer Science, Vol. 142, No. 1(1995), pp. 3–
26.
[ 2 ] Aho, A. V., Johnson, S. C., and Ullman,

Yamazaki ら [11] は LR 文法の文法仕様に対する構文検査を Scala, C++, Haskell の型システム上で実現する手法を提案した．この研究では，実際に ﬂuent

10

日本ソフトウェア科学会第 41 回大会 (2024 年度) 講演論文集

J. D.: Deterministic parsing of ambiguous grammars, Communications of the ACM, Vol. 18,
No. 8(1975), pp. 441–452.
[ 3 ] Alfred, V. A., Monica, S. L., and Jeﬀrey, D. U.:
Compilers principles, techniques & tools, pearson
Education, 2007.
[ 4 ] Gil, Y. and Roth, O.: Fling-A ﬂuent API generator, 33rd European Conference on Object-Oriented
Programming (ECOOP 2019), Schloss-DagstuhlLeibniz Zentrum für Informatik, 2019.
[ 5 ] Johnson, S. C. et al.: Yacc: Yet anothercompiler-compiler, Vol. 32, Bell Laboratories Murray Hill, NJ, 1975.
[ 6 ] Martin, F.: Fluent Interface, https://www.
martinfowler.com/bliki/FluentInterface.html. (Accessed on 07/29/2024).
[ 7 ] Nakamaru, T., Ichikawa, K., Yamazaki, T., andChiba, S.: Silverchain: a ﬂuent API generator, Proceedings of the 16th ACM SIGPLAN International
Conference on Generative Programming: Concepts

and Experiences, 2017, pp. 199–211.
[ 8 ] Palmkvist, V., Castegren, E., Haller, P., andBroman, D.: Statically Resolvable Ambiguity, Proceedings of the ACM on Programming Languages,Vol. 7, No. POPL(2023), pp. 1686–1712.
[ 9 ] Roth, O. and Gil, Y.: Fluent APIs in Functional Languages, Proceedings of the ACM on Programming Languages, Vol. 7, No. OOPSLA1(2023),
pp. 876–901.
[10] Yamazaki, T., Nakamaru, T., and Chiba, S.:
Yet Another Generating Method of Fluent Interfaces Supporting Flat-and Sub-Chaining Styles,
Proceedings of the 15th ACM SIGPLAN International Conference on Software Language Engineering, 2022, pp. 249–259.
[11] Yamazaki, T., Nakamaru, T., Ichikawa, K., andChiba, S.: Generating a ﬂuent API with syntax checking from an LR grammar, Proceedingsof the ACM on Programming Languages, Vol. 3,
No. OOPSLA(2019), pp. 1–24.