1. Forthの強みは"育てる設計"にある
Thinking Forth は一貫して次のように主張する。
「Forthプログラムは、完成品を一気に書くものではなく、
小さな道具を育て、それらが自然につながる形へ進化させるプロセスである。」
これは、一般的な "トップダウン設計" や"初期仕様を完璧に決めてから書く" アプローチとは異なる。
Forthは、
- 小さい語彙を試作し、
- 使ってみて問題点や不足を見つけ、
- 語彙を育てながら設計を進める、
というボトムアップ進化型設計が最も向いている。
2. ボトムアップ設計とは何か
小さなワードの実験から始める
トップダウンでは、"最終的にどうあるべきか"を先に決める。
しかし Forth の場合、複雑な処理を一気に語彙化するのは困難である。
Thinking Forth が推奨するのは:
- 作りたい処理の 低レベル断片 をまず実装し、
- REPL で動作を確認し、
- 有用な断片から語彙を育て、
- その語彙を組み合わせて大きな構造を作る、
というやり方である。
"語彙の発見" → "抽象化" → "統合" のサイクル
Thinking Forth は、語彙を"見つける"と言う。トップダウンで決めるのではなく、実験の中で発見される語彙があるからだ。
このサイクルは:
- 発見:繰り返し使う操作を見つける
- 抽象化:それに名前をつけてワード化する
- 統合:高レベルワードの一部として組み込む
例:CSV処理でよく出る断片
- 行を読む
- カンマで分割する
- 数値に変換する
これらを低レベル操作として見つけ、次に意味のある高レベル語彙として統合する。
3. 抽象化の基本:名前と責任の分離
Forthの抽象化は非常に強力である。なぜなら、構文と文法がシンプルなので、どんな処理でも自然に新しい語彙として切り出せるからだ。
抽象化の基本要素は「名前」「責任」「隠蔽」
Thinking Forth は次のように示す。
良い抽象化とは:
- 名前が適切(意味が明確である)
- 単一の責任(1つの目的に集中している)
- 内部処理を隠蔽(高レベル語彙では詳細が不要)
: DO-IT-ALL ( ... ) ... ;
- 内容が読めない
- 意図が曖昧
- 再利用不可
- 下位語彙が曖昧に隠れてしまう
: READ-CSV-LINE ( -- addr u ) ...
: SPLIT-COMMAS ( addr u -- addr1 u1 addr2 u2 ... ) ...
: TO-NUMBERS ( ... -- n1 n2 ... ) ...
: SUM-RECORD ( ... -- sum ) ...
高レベルでは次のように使える。
READ-CSV-LINE SPLIT-COMMAS TO-NUMBERS SUM-RECORD
文章のように "読める" のが重要である。
4. Thinking Forth 的:抽象化の7つの基準
Forthの抽象化判断の基準は、
-
その操作は"名前をつけられるか"
名前が自然につけられるなら抽象化すべきである。
-
その操作は複数の場所で出現するか
重複は語彙化の最大のサインである。
-
スタック操作が複雑になっていないか
ROT・SWAP・OVERの連続があるなら抽象化を検討する。
-
高レベル語彙に現れるべきか?
高レベル語彙は"意図"のみを書くべきであり、詳細は下位語彙が担当する。
-
下位語彙に隠したとき、意味が自然か?
抽象化が自然でないなら、上位語彙の粒度が間違っている。
-
その語彙は"テスト可能"か?
REPLで単体テストできなければ、責任が大きすぎる。
-
その語彙は他に再利用されそうか?
汎用語彙は積極的に切り出すべきである。
5. 実例:ボトムアップで語彙を育てるプロセス
以下に、実際のForth開発に近い"語彙発見の流れ"を示す。
STEP 1:まず低レベルの実験をする
ファイルから行を読み、パースし、数値にして、合計する処理を考える。
最初は実験的な断片を書く:
S" data.txt" R/O OPEN-FILE THROW
PAD 80 2 PICK READ-LINE THROW
PAD SWAP S>NUMBER? DROP
ここで、パターンが見つかる。
STEP 2:繰り返し現れるものを語彙化する
\ ファイルから1行読む
: READ-LINE$ ( fid -- addr u flag )
PAD 80 ROT READ-LINE THROW ;
\ 文字列を数値に変換
: TO-NUM ( addr u -- n )
S>NUMBER? 0= IF 0 THEN ; \ gforthの場合
STEP 3:語彙を組み合わせて"文章"を作る
: PROCESS-RECORD ( fid -- n flag )
READ-LINE$ IF TO-NUM TRUE ELSE DROP 0 FALSE THEN ;
STEP 4:高レベル語彙を作り直して整理する
: SUM-FILE ( fid -- total )
0 BEGIN
OVER PROCESS-RECORD WHILE
+
REPEAT
SWAP CLOSE-FILE THROW ;
この時点で「データを読み、変換し、加算する」という高レベルの意味が一気に表面化する。
これはトップダウンでは得られない"自然な構造"である。
6. 大規模構造の設計:語彙の連鎖としてのプロセス
Thinking Forth の核心は、
大規模な制御構造は、下位語彙の自然な連鎖として"現れるべきであり、あらかじめ図面として描く必要はない。"
さらに、Forthの辞書は柔軟なので、次のような進化的設計が可能である。
: PROCESS-FILE
READ-LINE PARSE SUM PRINT ;
必要に応じて、後で部分を差し替える(言語の成長)
: PARSE ( addr u -- parsed ) ... ;
: PARSE ( addr u -- parsed ) PARSE2 ; \ 改良版に差し替え
Forthはオブジェクト指向より"言語的成長"のほうが自然である。