---
title: テスト駆動開発から始める　なでしこ3 入門
tags:  #tdd #nadesiko3 #日本語プログラミング #自動化 #設計パターン  
author: [Katuyuki Kakigi](https://docswell.com/user/k2works)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/9J292XZ3ER.jpg?width=480
description: AI が生成したコードを読む負担を減らすため、 日本語でコードを書くことを提案し、 なでしこ3 を用いた FizzBuzz の例で TDD の Red‑Green‑Refactor サイクルを実演します。 また、 Nix や Makefile、 GitHub Actions を組み合わせた開発環境と自動化フロー、 辞書やクロージャを活用したクラス不要の構造化設計手法についても解説しています。
published: September 30, 26
canonical: https://docswell.com/s/k2works/KDMQWG-2026-09-30-124321
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/9J292XZ3ER.jpg)

テスト駆動開発から始める
なでしこ3 入門
AI 時代の「読む負担」を日本語コードで減らせるか
2026-09-30
1

# Page. 2

![Page Image](https://bcdn.docswell.com/page/DEY4Y8R8JM.jpg)

AI に書かせると、人間は読まされる
減った負担
・タイピング
・構文を思い出す
・定型コードを書く
・サンプルを探す
増えた負担
・生成コードを理解する
・意図と実装を照合する
・誤りを見つける
・修正の影響を読む
人間のボトルネックは、書くことから読むことへ移った。
2

# Page. 3

![Page Image](https://bcdn.docswell.com/page/VJNYPQD978.jpg)

ならば、日本語でコードを書けばいいんじゃね？
仮説：日本語で書けば、生成コードを読む
認知負荷を下げられるのではないか。
評価軸
確かめること
可読性
日本語の語順と助詞から値の役割を追えるか
検証可能性
テストも日本語の仕様として読めるか
設計力
辞書・関数で変更に耐える構造を作れるか
実用性
lint・format・CIまで自動化できるか
3

# Page. 4

![Page Image](https://bcdn.docswell.com/page/YE9P3DG3J3.jpg)

最初に見てほしい「日本語のコード」
●(Nを)FizzBuzz変換
Nを文字列変換して戻す
ここまで
「1を渡したら文字列1を返す」と
(1をFizzBuzz変換)と「1」で検証
・(Nを)と 1 をが対応する
・を が値と処理の関係を示す
・テストが、そのまま日本語の仕様として読める
日本語なら、本当に読みやすく、正しく、育てやすいのか？
4

# Page. 5

![Page Image](https://bcdn.docswell.com/page/GE8DM3VLED.jpg)

Python 参照：最初の関数とテスト
なでしこ3
Python / pytest
●(Nを)FizzBuzz変換
Nを文字列変換して戻す
ここまで
「1を渡したら文字列1を返す」と
(1をFizzBuzz変換)と「1」で検証
def fizz_buzz(n: int) -&gt; str:
return str(n)
def test_1を渡したら文字列1を返す():
assert fizz_buzz(1) == &quot;1&quot;
・Python は fizz_buzz(1) の位置で引数を渡す
・なでしこ3 は 1をFizzBuzz変換 の助詞で役割を示す
5

# Page. 6

![Page Image](https://bcdn.docswell.com/page/LELM3V5Q7R.jpg)

PART 1
TDD の基本サイクル
小さな仕様から始める
6

# Page. 7

![Page Image](https://bcdn.docswell.com/page/4JMYDGRKJW.jpg)

仕様を TODO リストへ分解する
・[] 1 を渡したら &quot;1&quot;
・[] 2 を渡したら &quot;2&quot;
・[] 3 の倍数なら Fizz
・[] 5 の倍数なら Buzz
・[] 15 の倍数なら FizzBuzz
・[] 1 から N までを一覧化する
一度に全部つくらない。次の一歩だけを選ぶ。
7

# Page. 8

![Page Image](https://bcdn.docswell.com/page/PJR94ZR679.jpg)

最初の Red
「1を渡したら文字列1を返す」と
(1をFizzBuzz変換)と「1」で検証
[文法エラー]
関数『FizzBuzz変換』が見つかりません。
・実装より先に、期待する振る舞いを書く
・関数が存在しないエラーも、立派な Red
・失敗を観察してから実装へ進む
8

# Page. 9

![Page Image](https://bcdn.docswell.com/page/PEXQ29RDJX.jpg)

Fake It — 最小の Green
●(Nを)FizzBuzz変換
「1」を戻す
ここまで
・正しい一般解を急がない
・最初のテストを通すだけの仮実装
・Green は次の変更に進める安全地点
「ズルでは？」――次のテストが、このズルを壊してくれる。
9

# Page. 10

![Page Image](https://bcdn.docswell.com/page/3EK9MPRDED.jpg)

Triangulation — 例を増やす
「1を渡したら文字列1を返す」と
(1をFizzBuzz変換)と「1」で検証
「2を渡したら文字列2を返す」と
(2をFizzBuzz変換)と「2」で検証
●(Nを)FizzBuzz変換
Nを文字列変換して戻す
ここまで
・2つ目の例が定数返しを壊し、一般化を導く
10

# Page. 11

![Page Image](https://bcdn.docswell.com/page/L73WYX8P75.jpg)

条件の順番も仕様である
●(Nを)FizzBuzz変換
もし、N%15=0ならば、「FizzBuzz」を戻す
もし、N%3=0ならば、「Fizz」を戻す
もし、N%5=0ならば、「Buzz」を戻す
Nを文字列変換して戻す
ここまで
・15 は 3 の倍数でも 5 の倍数でもある
・FizzBuzz を先に判定しなければならない
・小さな例が、隠れた優先順位を明らかにする
11

# Page. 12

![Page Image](https://bcdn.docswell.com/page/87DK52M3JG.jpg)

Python 参照：FizzBuzz の条件分岐
def fizz_buzz(n: int) -&gt; str:
if n % 15 == 0:
return &quot;FizzBuzz&quot;
if n % 3 == 0:
return &quot;Fizz&quot;
if n % 5 == 0:
return &quot;Buzz&quot;
return str(n)
def test_fizz_buzz():
assert fizz_buzz(3) == &quot;Fizz&quot;
assert fizz_buzz(5) == &quot;Buzz&quot;
assert fizz_buzz(15) == &quot;FizzBuzz&quot;
・条件の順番という仕様は、言語が変わっても同じ
12

# Page. 13

![Page Image](https://bcdn.docswell.com/page/VJPKG1RPE8.jpg)

Obvious Implementation
●(Nまで)FizzBuzz配列作成
結果 = []
Iを1からNまで繰り返す
結果に(IをFizzBuzz変換)を配列追加
ここまで
結果を戻す
ここまで
・実装が明白なら、素直に書いてテストする
・仮実装だけが TDD ではない
・変換と一覧生成の責務を分ける
13

# Page. 14

![Page Image](https://bcdn.docswell.com/page/2EVV6YRVEQ.jpg)

Python 参照：1 から N までを変換する
なでしこ3
Python
●(Nまで)FizzBuzz配列作成
結果 = []
Iを1からNまで繰り返す
結果に(IをFizzBuzz変換)を配列追加
ここまで
結果を戻す
ここまで
def fizz_buzz_list(n: int) -&gt; list[str]:
result = []
for i in range(1, n + 1):
result.append(fizz_buzz(i))
return result
・Nまで と range(1, n + 1) に境界表現の違いが出る
14

# Page. 15

![Page Image](https://bcdn.docswell.com/page/57GLN9M1EL.jpg)

Red → Green → Refactor
Red
失敗するテスト
Green
最小の実装
Refactor
動作を保って改善
TODO がなくなるまで、小さく反復する。
15

# Page. 16

![Page Image](https://bcdn.docswell.com/page/4EQYQWRNJP.jpg)

第 1 部で得たもの
手段
得られたもの
TODO リスト
大きな仕様を小さくする
Test First
期待を実装より先に固定する
Fake It
最小の Green を得る
Triangulation
例から一般化を導く
Refactor
動作を保ったまま構造を改善する
成果：日本語で読める、テストされた FizzBuzz
16

# Page. 17

![Page Image](https://bcdn.docswell.com/page/KJ4WNK8371.jpg)

PART 2
開発環境と自動化
手元の成功を、チームの規律へ
17

# Page. 18

![Page Image](https://bcdn.docswell.com/page/LE1Y5X2Z7G.jpg)

テストが通ったら履歴を残す
変更確認 → 差分確認 → ステージ → コミット
TDD の動き
Conventional Commits
テストを追加
test:
振る舞いを追加
feat:
構造を改善
refactor:
環境を整備
build:
・小さなテスト単位と、小さなコミット単位をそろえる
・履歴から「何を確かめ、何を変えたか」を読める
18

# Page. 19

![Page Image](https://bcdn.docswell.com/page/GEWG5NR6J2.jpg)

再現できる開発環境
nix develop .#nadesiko
gonako --version
make check
・gonako 3.8.8 を固定
・Nix で処理系と Go のバージョンをそろえる
・誰もが同じ入口から、同じコマンドを実行する
「自分の環境では動く」を、環境定義で減らす。
19

# Page. 20

![Page Image](https://bcdn.docswell.com/page/47ZLN3RRJ3.jpg)

4 つの品質チェック
チェック
コマンド
守るもの
Unit Test
make test
関数の振る舞い
Lint
make lint
文法・識別子・未定義語
Format
make format-check
書式の一貫性
Doctest
make doctest
表示・受け入れ結果
make check
単体テストだけでは、出力や整形差分までは守れない。
20

# Page. 21

![Page Image](https://bcdn.docswell.com/page/YJ6W8XR1JV.jpg)

テストランナーを自分で育てる
ファイルの中
・検証 で成功・失敗を数える
・失敗内容を日本語で表示
・最後に終了コードへ反映
ファイルの外
・Makefile が *_test.nako3 を列挙
・1 ファイルずつ実行
・全体の成功・失敗を集計
テストの仕組み自体も、育てるプロダクトである。
21

# Page. 22

![Page Image](https://bcdn.docswell.com/page/GJ5M9X8LJ4.jpg)

CI はローカルの約束を繰り返す
開発者
GitHub Actions
make check
lint / format / test / doctest
・ローカルと CI で同じコマンドを使う
・push / pull request ごとに品質ゲートを再実行する
22

# Page. 23

![Page Image](https://bcdn.docswell.com/page/9E292XK37R.jpg)

第 2 部で得たもの
・Git と Conventional Commits で変更の意図を残す
・Nix で処理系を固定する
・Makefile で品質チェックの入口を一つにする
・GitHub Actions で約束を繰り返す
成果：誰が実行しても同じ判定になる開発フロー
23

# Page. 24

![Page Image](https://bcdn.docswell.com/page/D7Y4Y8N8EM.jpg)

PART 3
構造化設計
クラスがなくても設計できる
24

# Page. 25

![Page Image](https://bcdn.docswell.com/page/VENYPQX9J8.jpg)

新しい要求：変換タイプを切り替える
・通常：3 → Fizz
・数字限定：3 → 3
・FizzBuzz 限定：3 → 3、15 → FizzBuzz
困ったこと
・条件分岐を利用側へ散らしたくない
・なでしこ3 にはクラス構文がない
使える道具
辞書・関数参照・無名関数・クロージャ
25

# Page. 26

![Page Image](https://bcdn.docswell.com/page/Y79P3D83E3.jpg)

辞書によるポリモーフィズム
●(番号から)タイプ生成
もし、番号 = 1ならば
{&quot;名前&quot;: &quot;通常&quot;,
&quot;変換&quot;: {関数}FizzBuzz変換}を戻す
違えば、もし、番号 = 2ならば
{&quot;名前&quot;: &quot;数字限定&quot;,
&quot;変換&quot;: {関数}数字限定変換}を戻す
ここまで
ここまで
・属性は辞書の値
・振る舞いは辞書に格納した関数
・利用側は同じ 変換 だけを見る
26

# Page. 27

![Page Image](https://bcdn.docswell.com/page/G78DM38L7D.jpg)

Python 参照：辞書に関数を格納する
from collections.abc import Callable
type Converter = Callable[[int], str]
def create_type(number: int) -&gt; dict[str, str | Converter]:
if number == 1:
return {&quot;name&quot;: &quot;通常&quot;, &quot;convert&quot;: fizz_buzz}
if number == 2:
return {&quot;name&quot;: &quot;数字限定&quot;, &quot;convert&quot;: str}
raise ValueError(f&quot;該当するタイプは存在しません: {number}&quot;)
def convert(type_: dict, n: int) -&gt; str:
converter = type_[&quot;convert&quot;]
return converter(n)
・Python でも関数は値。辞書へ格納して差し替えられる
・なでしこ3 は {関数}FizzBuzz変換 で関数参照を明示する
27

# Page. 28

![Page Image](https://bcdn.docswell.com/page/L7LM3VRQJR.jpg)

パターンは問題から導入する
問題
パターン
なでしこ3 での表現
数値と結果がばらばら
Value Object
2 値を持つ辞書
生の配列を直接操作
First-Class Collection
配列を包む辞書と関数
操作ごとに呼び方が違う
Command
実行 関数を持つ辞書
パターン名から始めない。テストで見えた責務から導入する。
28

# Page. 29

![Page Image](https://bcdn.docswell.com/page/4EMYDGPKEW.jpg)

Command とクロージャ
●(タイプで)値コマンド作成
処理 = 関数(N)
[タイプでNからFizzBuzz値作成]を戻す
ここまで
{&quot;名前&quot;: &quot;値コマンド&quot;, &quot;実行&quot;: 処理}を戻す
ここまで
・無名関数が作成時の タイプ を覚える
・値コマンドもリストコマンドも配列を返す
・戻り値の形をそろえ、利用側の分岐をなくす
29

# Page. 30

![Page Image](https://bcdn.docswell.com/page/PER94Z16J9.jpg)

Python 参照：Command とクロージャ
from collections.abc import Callable
def create_value_command(type_: dict) -&gt; dict[str, object]:
def execute(n: int) -&gt; list[dict]:
return [create_value(type_, n)]
return {&quot;name&quot;: &quot;値コマンド&quot;, &quot;execute&quot;: execute}
def execute(command: dict, n: int) -&gt; list[dict]:
operation: Callable = command[&quot;execute&quot;]
return operation(n)
・内側の execute が外側の type_ を覚える
・クロージャの役割は同じ。構文と呼び出し順が異なる
30

# Page. 31

![Page Image](https://bcdn.docswell.com/page/P7XQ29YDEX.jpg)

SOLID を構文ではなく責務として読む
原則
この実装での現れ方
SRP
変換・値・一覧・コマンドを分離
OCP
タイプを追加しても利用側を変えない
LSP
どのタイプも同じ変換規約で置換可能
ISP
必要最小限の関数・辞書項目を公開
DIP
具体的な変換でなく、渡された関数へ依存
動的型付けでは、テストが契約を支える。
31

# Page. 32

![Page Image](https://bcdn.docswell.com/page/37K9MP3D7D.jpg)

モジュール構成が設計図になる
src/
├── fizzbuzz.nako3 # 基本変換
├── type.nako3 # タイプとディスパッチ
├── value.nako3 # 値オブジェクト
├── list.nako3 # コレクション
└── command.nako3 # 操作
・名前と配置で責務の境界を見えるようにする
・依存の向きを単純に保つ
・テストも責務ごとに分割する
32

# Page. 33

![Page Image](https://bcdn.docswell.com/page/LJ3WYXGPJ5.jpg)

第 3 部で得たもの
・クラス構文の有無と、よい設計は別問題
・辞書と関数でデータと振る舞いを組み合わせる
・パターンと SOLID で変更の影響を閉じ込める
・テストを動的な契約として使う
成果：要求追加に開いた FizzBuzz の構造
33

# Page. 34

![Page Image](https://bcdn.docswell.com/page/8JDK5213EG.jpg)

PART 4
関数型スタイルへ
「何をするか」をつなぐ
34

# Page. 35

![Page Image](https://bcdn.docswell.com/page/VEPKG1QP78.jpg)

関数を値として扱う
規則 = {関数}FizzBuzz変換
規則(15)
●(規則でNを)規則適用
規則(N)を戻す
ここまで
・{関数} で関数そのものを取り出す
・引数として渡し、あとで呼ぶ
・名前付き関数・無名関数・クロージャを同じ値として扱う
35

# Page. 36

![Page Image](https://bcdn.docswell.com/page/27VV6Y5V7Q.jpg)

map / filter / composition
●(規則でNまで)一括変換
規則を(1からNまでの配列連番作成)へ配列マップ
ここまで
●(判定で配列を)絞り込み
判定で配列を配列フィルタ
ここまで
・配列マップ：各要素を変換した新しい配列
・配列フィルタ：条件に合う要素だけを残す
・ループを「対象に規則を適用する」という宣言へ変える
36

# Page. 37

![Page Image](https://bcdn.docswell.com/page/5JGLN9617L.jpg)

Python 参照：高階関数と map / filter
from collections.abc import Callable, Iterable
def apply_rule(rule: Callable[[int], str], n: int) -&gt; str:
return rule(n)
def convert_all(rule: Callable[[int], str], n: int) -&gt; list[str]:
return list(map(rule, range(1, n + 1)))
def select(
predicate: Callable[[int], bool], values: Iterable[int]
) -&gt; list[int]:
return list(filter(predicate, values))
・Python も関数を引数として渡せる
・規則でNまで一括変換 は、助詞で 2 つの引数の役割を分ける
37

# Page. 38

![Page Image](https://bcdn.docswell.com/page/47QYQW4NEP.jpg)

Haskell 参照：高階関数と map / filter
type Rule = Int -&gt; String
applyRule :: Rule -&gt; Int -&gt; String
applyRule rule n = rule n
convertAll :: Rule -&gt; Int -&gt; [String]
convertAll rule n = map rule [1 .. n]
select :: (a -&gt; Bool) -&gt; [a] -&gt; [a]
select = filter
・関数の型 Int -&gt; String をそのまま別名にできる
・カリー化により、引数を 1 つずつ適用する
・map / filter は新しいリストを返す
38

# Page. 39

![Page Image](https://bcdn.docswell.com/page/KE4WNKQ3J1.jpg)

日本語のパイプライン
●(Nを)FizzBuzz装飾
NをFizzBuzz変換して装飾して戻す
ここまで
入れ子
「～して」
装飾(FizzBuzz変換(N))
NをFizzBuzz変換して装飾
内側から外側へ読む
処理順に左から右へ読む
39

# Page. 40

![Page Image](https://bcdn.docswell.com/page/L71Y5XPZJG.jpg)

パイプライン参照：F# / Elixir / Clojure
F# |&gt;
Elixir |&gt;
Clojure -&gt;
let FizzBuzz装飾 n =
n
|&gt; FizzBuzz変換
|&gt; 装飾
def fizz_buzz_decorated(n) do
n
|&gt; fizz_buzz()
|&gt; decorate()
end
(defn fizz-buzz-decorated [n]
(-&gt; n
fizz-buzz
decorate))
値を次の関数の
最後の引数へ渡す
値を次の関数の
第 1 引数へ渡す
スレッディングマクロが
値を先頭引数へ挿入する
3 言語とも左から右へ値が流れる。なでしこ3 は「～して」自体が日本語の接続として読める。
40

# Page. 41

![Page Image](https://bcdn.docswell.com/page/G7WG5N26E2.jpg)

Python 参照：パイプラインと不変データ
def decorate(value: str) -&gt; str:
return f&quot;[{value}]&quot;
def decorated_fizz_buzz(n: int) -&gt; str:
return decorate(fizz_buzz(n))
def pipeline(n: int) -&gt; list[str]:
return list(map(decorated_fizz_buzz, range(1, n + 1)))
def appended(values: list[int], x: int) -&gt; list[int]:
return [*values, x]
・Python の関数合成は内側から外側へ読む
・なでしこ3 の「～して」は処理順に左から右へ読める
41

# Page. 42

![Page Image](https://bcdn.docswell.com/page/4JZLN3DRE3.jpg)

Haskell 参照：関数合成と不変データ
decorate :: String -&gt; String
decorate value = &quot;[&quot; ++ value ++ &quot;]&quot;
decoratedFizzBuzz :: Int -&gt; String
decoratedFizzBuzz = decorate . fizzBuzz
pipeline :: Int -&gt; [String]
pipeline n = map (decorate . fizzBuzz) [1 .. n]
appended :: [a] -&gt; a -&gt; [a]
appended values x = values ++ [x]
・(.) が右から左へ関数を合成する
・値は変更せず、常に新しい値を返す
・不変性は規約ではなく、言語の基本モデル
42

# Page. 43

![Page Image](https://bcdn.docswell.com/page/YE6W8X11EV.jpg)

不変データで流れを追いやすくする
●(配列にxを)追加した配列
新配列 = 配列を配列複製
新配列にxを配列追加
新配列を戻す
ここまで
・元の配列を変更せず、新しい配列を返す
・各段階が「入力 → 新しい出力」になる
・テストで元データが変わらないことも確かめる
変更箇所を探す負担が減り、処理の流れを追いやすくなる。
43

# Page. 44

![Page Image](https://bcdn.docswell.com/page/GE5M9X4LE4.jpg)

エラーも値として返す
{&quot;成功&quot;: はい, &quot;値&quot;: 値}
{&quot;成功&quot;: いいえ, &quot;エラー&quot;: メッセージ}
●(Nを)安全変換
もし、(Nの変数型確認) ≠ 「number」ならば
「数値を指定してください: {N}」の失敗結果を戻す
ここまで
(NをFizzBuzz変換)の成功結果を戻す
ここまで
・成功と失敗を同じ形の結果辞書で扱う
44

# Page. 45

![Page Image](https://bcdn.docswell.com/page/97292XV3JR.jpg)

Python 参照：結果を値として返す
from typing import TypedDict
class Result(TypedDict, total=False):
success: bool
value: str
error: str
def safe_convert(value: object) -&gt; Result:
if not isinstance(value, int):
return {&quot;success&quot;: False, &quot;error&quot;: f&quot;数値を指定してください: {value}&quot;}
if value &lt;= 0:
return {&quot;success&quot;: False, &quot;error&quot;: f&quot;正の数を指定してください: {value}&quot;}
return {&quot;success&quot;: True, &quot;value&quot;: fizz_buzz(value)}
・Python では isinstance、なでしこ3 では 変数型確認
・どちらも成功/失敗を呼び出し側が明示的に扱える
45

# Page. 46

![Page Image](https://bcdn.docswell.com/page/DJY4Y8387M.jpg)

Haskell 参照：Either で失敗を型にする
safeConvert :: Int -&gt; Either String String
safeConvert n
| n &lt;= 0 = Left (&quot;正の数を指定してください: &quot; ++ show n)
| otherwise = Right (fizzBuzz n)
strictConvert :: Int -&gt; String
strictConvert n =
case safeConvert n of
Right value -&gt; value
Left message -&gt; error message
・Right が成功、Left が失敗
・呼び出し側はパターンマッチで両方を扱う
・引数が Int なので、文字列の誤入力はコンパイル時に除外される
46

# Page. 47

![Page Image](https://bcdn.docswell.com/page/V7NYPQV9E8.jpg)

動的型付けで安全性をつくる
1. 関数の入口で 変数型確認
2. 想定外の値を失敗結果にする
3. 厳格変換 で失敗を例外へ変換できる
4. 空許容変換 で Option 相当を表現する
5. 値だけでなく型もテストする
型安全性は「静的型の有無」だけでなく、境界設計の問題でもある。
47

# Page. 48

![Page Image](https://bcdn.docswell.com/page/YJ9P3D6373.jpg)

Option 相当を表現する
なでしこ3：空
Scala : Option
Kotlin : null 許容型
●(Nを)空許容変換
もし、N = 空ならば
空を戻す
ここまで
NをFizzBuzz変換して戻す
ここまで
def 空許容変換(
値: Option[Int]
): Option[String] =
値.map(FizzBuzz変換)
val なし = 空許容変換(None)
val あり = 空許容変換(Some(3))
fun 空許容変換(
値: Int?
): String? =
値?.let(::FizzBuzz変換)
val なし = 空許容変換(null)
val あり = 空許容変換(3)
・3つとも「値がない」状態を表現する
・Scala と Kotlin は、値がない可能性を型に含める
・失敗理由が必要なら Either・Result・判別共用体を使う
48

# Page. 49

![Page Image](https://bcdn.docswell.com/page/GJ8DM3ZLJD.jpg)

静的型付けで安全性をつくるには
F#：判別共用体で成功/失敗を表現する
type 変換結果 =
| 成功 of string
| 失敗 of string
let 安全変換 (値: int) : 変換結果 =
if 値 &lt;= 0 then
失敗 $&quot;正の数を指定してください: {値}&quot;
else
成功 (FizzBuzz変換 値)
let 結果表示 結果 =
match 結果 with
| 成功 値 -&gt; 値
| 失敗 メッセージ -&gt; $&quot;エラー: {メッセージ}&quot;
・成功と失敗以外の状態を作れない
・パターンマッチで両方を扱わないと、コンパイラが警告する
・値: int により、文字列の誤入力はコンパイル時に除外される
49

# Page. 50

![Page Image](https://bcdn.docswell.com/page/LJLM3VDQER.jpg)

第 4 部で得たもの
・関数を値として渡し、振る舞いを組み立てる
・パイプラインで処理順を左から右へ読む
・不変データで変更の追跡を簡単にする
・エラーを値にして、失敗も合成する
成果：日本語の語順を活かした宣言的な FizzBuzz
50

# Page. 51

![Page Image](https://bcdn.docswell.com/page/47MYDGWK7W.jpg)

12 章で育ったもの
部
出発点
到達点
TDD の基本
未定義の 1 関数
テストされた FizzBuzz
開発環境
手元での実行
再現可能な品質ゲート
構造化設計
条件分岐
交換可能な設計
関数型スタイル
可変なループ
合成可能なパイプライン
例 → テスト → 実装 → 自動化 → 設計 → 合成
51

# Page. 52

![Page Image](https://bcdn.docswell.com/page/P7R94ZY6E9.jpg)

最大の気づき
「日本語」より「助詞」が効いていた
FizzBuzz変換(3) # 役割を関数名と位置から推測
3をFizzBuzz変換 # 「を」が値と変換の関係を示す
検証(名前, 実際, 期待)
名前と実際と期待で検証 # 「と」「で」が役割を示す
助詞は飾りではない。コード理解のインターフェースとして働く。
52

# Page. 53

![Page Image](https://bcdn.docswell.com/page/PJXQ29GD7X.jpg)

でも、日本語の識別子なら他言語でも書ける
日本語化スコア: なでしこ3 と 17 言語平均
・ほかの 17 言語でも実装して比較
・17 言語中 16 言語で日本語識別子を利用可能
・平均が低いのは名前の 2 軸
・語順とテスト名は言語機能で補いやすい
各軸 0～3 点。出典：多言語統合解説 第 9 章
53

# Page. 54

![Page Image](https://bcdn.docswell.com/page/3JK9MP4DJD.jpg)

メジャー言語でも「助詞らしい見た目」は作れる
// Kotlin
infix fun &lt;T, R&gt; T.を(動詞: (T) -&gt; R): R = 動詞(this)
infix fun &lt;T, R&gt; T.して(動詞: (T) -&gt; R): R = 動詞(this)
fun FizzBuzz装飾(数: Int): String =
数 を ::FizzBuzz変換 して ::装飾
・TypeScript：メソッドチェーン
・Python：演算子多重定義
・Rust：宣言的マクロ
・F# / Elixir：パイプライン
54

# Page. 55

![Page Image](https://bcdn.docswell.com/page/LE3WYXLPE5.jpg)

決定的な違い
助詞を「置ける」か、言語が「理解する」か
・他言語：助詞を関数・演算子・マクロとして自作する
・なでしこ3：●(Nを)FizzBuzz変換 が言語の文法
・を・に・でが、引数の役割を決める
・Unicode の日本語名と、日本語の文法は同じではない
日本語で命名できる言語は多い。
日本語の文法で書けるのが、なでしこ3 の違いである。
55

# Page. 56

![Page Image](https://bcdn.docswell.com/page/8EDK52N37G.jpg)

日本語化には代償もある
観点
起きること
大文字・小文字
公開規則・型名・定数名と衝突
lint
ASCII 前提の命名規則を調整する必要
ロケール
UTF-8 でないとファイル名やテスト検出が壊れる
Unicode 正規化
見た目が同じ名前が同一/別物になり得る
開発体験
IME、検索、補完、全角・半角の揺れ
書けることと、チームで書くべきことは分けて考える。
56

# Page. 57

![Page Image](https://bcdn.docswell.com/page/V7PKG15PJ8.jpg)

評価結果：読む負担は減るのか
・読解：助詞が値の役割を示し、処理を日本語順に追える
・検証：テスト名と検証式が日本語の仕様になる
・設計：辞書と関数で変更可能な構造を作れる
・自動化：lint・format・doctest・CI まで構築できる
・限界：境界条件・副作用・型の誤りは消えない
読む負担を下げたのは日本語の単語だけではなく、助詞による関係の明示だった。
57

# Page. 58

![Page Image](https://bcdn.docswell.com/page/2JVV6YPVJQ.jpg)

もっと詳しく知りたいなら
テスト駆動開発から始める なでしこ3 入門
・TODO リストから始める Red → Green → Refactor
・Git・Nix・Makefile・GitHub Actions による開発環境
・辞書と関数によるポリモーフィズム、デザインパターン、SOLID
・高階関数、不変データ、パイプライン、エラーハンドリング
・全 12 章の解説と実行可能なサンプルコード
https://k2works.github.io/getting-started-tdd/article/nadesiko/
テストを手がかりに、日本語コードの設計を段階的に育てます。
58

# Page. 59

![Page Image](https://bcdn.docswell.com/page/5EGLN9P1JL.jpg)

ご清聴ありがとうございました
日本語で読める。テストで信頼できる。
59

