はじめに
業務で「機能の羅列」を作る日々に退屈していませんか?
ダッシュボード、ログイン、決済……それらは「部品」ではあっても、システムとしての「理(ことわり)」を感じることは稀です。
今回は、FizzBuzzを単なる if 文のループとして書くのではなく、**「特定の数値を特定の記号に変換する『意味論』を持った最小の言語処理系」**として再定義してみます。
思考のプロセス:機能からシステムへ
通常のFizzBuzzが「手続き」であるのに対し、この実装では以下の3つの要素からなる**「世界(システム)」**を構築します。
項(Term)の定義: 世界に存在する要素(数値、記号、リスト)を型で定義する。
簡約規則(Reduction): 数値がFizzやBuzzという「意味」に変換される物理法則を定義する。
評価(Evaluation): 構造がどれほど深くても、再帰的に法則を適用し続ける仕組みを定義する。
module Program
type Value =
| Number of int
| Symbol of string
| List of Value list
let rec eval (expr: Value) : Value =
match expr with
| Number n -> apply_rule n
| Symbol s -> Symbol s
| List args ->
let argv = args |> List.map eval
List argv
and apply_rule (n: int) : Value =
let mzero3 = (n % 3 = 0)
let mzero5 = (n % 5 = 0)
match (mzero3, mzero5) with
| (true, true) -> Symbol "FizzBuzz"
| (true, false) -> Symbol "Fizz"
| (false, true) -> Symbol "Buzz"
| (false, false) -> Number n
let toNum (i: int) : Value =
Number i
let fizzbuzz (l: list<int>) : Value =
l
|> List.map toNum
|> List
|> eval
[<EntryPoint>]
let main args =
let result = fizzbuzz [1..100]
printfn "%A" result
0
この実装の面白いところ
パターンマッチングによる「制度設計」
match (mzero3, mzero5) の部分は、F#のコンパイラが「4つのケースすべてを網羅しているか」をチェックします。これは、バグの入り込む隙のない「完璧な制度」を記述している感覚に近いです。
構造の再帰性
この eval は、単なる1次元のリストだけでなく、List [Number 3; List [Number 5; Number 15]] のような入れ子構造であっても、どこまでも深く潜って FizzBuzz を適用し続けます。これは単なる「処理」ではなく「言語処理系」の振る舞いです。
意味論の分離
「何を表示するか」という表示ロジックと、「この数値はどういう意味を持つか」という簡約ロジックが分離されています。五十嵐淳先生の『プログラミング言語の基礎概念』で語られるような、プログラミング言語の本質的な楽しさに触れることができます。
おわりに
「機能」を作る開発に疲れたときは、一度立ち止まって「システム(体系)」を作ってみるのがおすすめです。
たとえ FizzBuzz のような小さな題材でも、独自の Value を定義し、自分だけの eval を走らせることで、コードが「生命(動的な実行)」を宿す喜びを再発見できるはずです。