- 関数型プログラミング(言語)の愛好者/実践者
- 関数型まつり2026の運営スタッフ(座長のひとり)
- 主に(CfPを含む)メインコンテンツの準備を担当
- 株式会社スマートラウンドのシニアエンジニア
- スタートアップと投資家のやり取りを効率化するデータ管理プラットフォームを開発
- ソフトウェア設計/開発とともに会計/財務/法務について探求するのも好き
[PR] 2026年7月の関数型まつり2026で登壇予定🐬
速習FP in Scala with Flix: Flix言語で親しむ純粋関数型のコード設計
[PR] Clojurian視点でのFlixの紹介記事を書きました🐬
Clojurianが出会ったsimple志向なJVM関数型言語Flix ——ファーストインプレッション
Fun Fun Functional (2) 関数型言語
Lightning Talks!! (2019年7月)での発表
"Simple Made Easy" Made Easy: Clojureの設計思想を理解しよう
[書き起こし記事] "simple"と"easy"はどう違う? Simple Made Easyを解説 Part1
(-> (simple-made-easy-made-easy)
revisit ; 「"Simple Made Easy" Made Easy」をおさらい
deepen ; 近年のClojureエコシステム/コミュニティの進化を深掘り
extend) ; Clojureの範囲に留まらないより一般的な示唆へと展開-
simple と easy という言葉
-
"Simple Made Easy"とは
-
simple と easy の意味
-
simple と easy の関係
-
なぜ simplicity を重視するのか
-
どのように simple にするのか
-
Clojureに見る simplicity
-
simple と easy の区別のより抽象的(メタ)な示唆
-
明確に定義された特別な意味がある
-
easy と混同されやすいが概念的にまったく違う
-
Clojureとは simple さ(simplicity)を追求した言語
-
-
あらゆる場面で非常に重視されている概念
-
設計の良し悪しを図る指標のひとつ
Simple Made Easy (at Strange Loop 2011)
-
Clojure作者Rich Hickeyによる極めて有名な発表
-
コミュニティで simple の概念が浸透するきっかけ
-
日本ではタイトルも趣旨も(特にClojurian以外から)わりと誤解されているかも
-
cf. Simplicity Matters (at RailsConf 2012)
-
敢えて訳せば「simple簡単解説」
- 例えば Word Power Made Easy という本がある
- 英語の文法的には:
- X makes Y easy.
- → Y is made easy [by X].
- → Y made easy [by X] (簡単になったY)
- → Y is made easy [by X].
- X makes Y easy.
-
本題の simple と easy を掛けた言葉遊びでもある
-
simple を追求すると easy にもなる?
- 英語的にも内容的にもちょっと苦しい解釈
-
simple<simplex(=sim-+plex)- ラテン語
simplex- 原義: 「一折り」「一編み」「一捻り」
- 「単一の役割」「単一の責務」「単一の概念」「単一の次元」 ※「単一の実体/操作」 ではない
- 客観的な概念: 設計対象の構造の単純さ
- ラテン語
-
対義語:
complex(=com-+plex)- 原義: 「絡まり合った」「折り重なった」「もつれた」
-
easy(=ease+-y) <aise/eise<adjacens- ラテン語
adjacens(cf. 🇬🇧adjacent)- 原義: 「近くにある」「隣接した」
- 「身近な」(物理的に近い)「親しみがある」(自分の理解に近い)「易しい」(できることに近い)
- 主観的/相対的な概念: 利用者からの(広義の)近さ
- ラテン語
-
対義語:
hard- 原義: 「強い」「手ごわい」 ※
easyとは無関係
- 原義: 「強い」「手ごわい」 ※
-
simple/complex, easy/hard の直交した2つの評価軸
- 単純なトレードオフの関係にはない
- simple AND easy も complex AND hard もある
-
「 easy よりも simple を選ぼう」 ではない
- complex になりうるものを避けて simple を追求しようというのがRich Hickeyの主張
- 選択するのは simple OR complex であって simple OR easy ではない
- simple AND easy は十分に実現可能
-
ease を優先し complexity (↔ simplicity)を甘受する
- 短期: easy なため生産性が高く、 complex であることも直ちに支障をきたさない
- 中長期: complex ゆえに生産性が急速に低下
- 構造的な限界により徐々に手に負えなくなる
-
⭐️ simplicity を優先し hardness (↔ ease)を甘受する
- 短期: hard ゆえに生産性が低く、 simple なものを課題に適用するにはいくらか時間が掛かる
- 中長期: simple なため生産性が高い水準を維持
- hard から easy へと習熟/環境整備も進む
"Simple Made Easy"というRich Hickeyによる標語がある
-
「 simple 簡単解説」というのが文字通りの解釈
-
simple と easy の峻別という本題に関わる最重要のキーワードが見事に組み込まれた表現
-
しかし、タイトル自体に何かを促すメッセージ性があるとは読み取りがたい
- simple: 覚えることの少なさ重視
- easy: 手数の少なさ重視
-
simplicity と ease がもたらしうる実用的メリットを分かりやすく伝えようという試みに感じられる
-
しかし、 simplicity が客観的な構造の問題から主観的な体験の問題に変質してしまう危うさがある
- 「覚えることが少ない」という体験はむしろ easy であることの例と位置付けるのが素直そう
simple と easy はトレードオフなので simple を選ぼう
- simple にすると ease が損なわれ、 easy にすると simplicity が損なわれるだろうか?
-
確かに、 simple but hard なもの、 easy but complex なものは両極端で分かりやすい対比
-
しかし、「設計対象の構造の単純さ」と「利用者からの(広義の)近さ」というまったく異なる性質の間に不可避的な連動関係があるとは考えがたい
-
- simplicity を分かりやすく(easy に)理解しようとするあまり、 simplicity が何かと complect している
-
例1: (例えば) simple にすると easy になるという論理関係が加わっている
-
例2: simplicity に主観的な要素が加わっている
-
例3: simple/complex, easy/hard という独立した評価軸が単一軸の二項対立にまとまっている
-
-
理解することなく信頼できるものは作れない
-
一度にごくわずかな数のことしかできない
-
絡まったものは一緒に考えるしかない
-
複雑さ(complexity)は理解を損なわせる
⇒ いわゆる認知負荷(cognitive load)による制約
-
構造的に(必然的に) — simplicity の内在的性質
- 組み合わせやすい(composable)
- 独立性が高い(modular)
-
人間的に(結果的に) — 派生的な ease への好影響
- 理解しやすい
- 変更しやすい
- デバッグしやすい
- 柔軟
- あとから方針/決定を見直すことも容易
- 置き換えられる
-
simple なものを見極める
- complexity をもたらす(
complectしている)ものを避ける
- complexity をもたらす(
-
もともと simple なものを利用する
-
抽象化する(
abstract)- abstractの原義は「引き離す」(draw away)
- 詳細を覆い隠すことではない
- who, what, when, where, why, howをそれぞれ検討することがヒントになるかも
-
あらかじめ問題を simple にする(
simplify)
;; Common Lisp: Clojure以前からあるLispの例
(defun fizzbuzz (n)
(let ((fizzp (zerop (mod n 3)))
(buzzp (zerop (mod n 5))))
(cond ((and fizzp buzzp) "Fizz Buzz")
(buzzp "Buzz")
(fizzp "Fizz")
(t n))))- 丸括弧が複数の役割を担って complect している
- as code:
- オペレーター呼び出し
- 束縛フォーム
condの節など、特殊形式/マクロの要素
- as data: コンスセル(によるリスト/alist/plistなど)
- as code:
;; Clojure
(defn fizzbuzz [n]
(let [fizz? (zero? (mod n 3))
buzz? (zero? (mod n 5))]
(cond
(and fizz? buzz?) "Fizz Buzz"
buzz? "Buzz"
fizz? "Fizz"
:else n)))- 括弧を役割ごとに使い分けたほうが simple
( ): オペレーター呼び出し(as code)、リスト(as data)[ ]: 束縛フォーム(as code)、ベクター(as data){ }: メタデータ(as code)、マップ(as data)
- ℹ️ Common Lispでもリーダーマクロによって括弧の意味付けの変更は可能
// Scala: オブジェクト指向言語の例
case class Coffee(price: Double):
def applyTax(rate: Double): Coffee =
copy(price = price * (1 + rate))- オブジェクト(クラス)に以下が complect している
- 値(value): それ自体としては不変のデータ
- [ミュータブルな場合] 状態(state): アイデンティティに紐付く値が時とともに変化するもの
- [メソッドを持つ場合] 関数: オブジェクトの文脈でのクロージャー
- 名前空間/モジュール: 定義を束ねるもの
;; Clojure
;; (require '[lagenorhynque.cafe.coffee :as-alias coffee])
(defn make-coffee [price]
#::coffee{:price price})
(defn apply-tax [coffee rate]
(update coffee ::coffee/price * (+ 1 rate)))- Clojureでは構造的に分離されていて simple
- 値(value): ただの不変のデータ
- [状態(state)を扱いたい場合] 次節で紹介 💁♂️
- 関数: ただのトップレベル関数
- 名前空間/モジュール:
nsとして独立
- ℹ️ Scalaの拡張メソッドにより(メソッドとしての)関数の分離は部分的に可能
// Scala: (可変の)変数を持つ言語の例
def heavyCalculation(xs: List[Int]): Int =
var result = 0 // 変数を宣言/初期化
for x <- xs do
result += x * x // 変数に再代入
result // 変数を参照- (可変の)変数に以下が complect している
- アイデンティティ: 値が変化しても変わらない同一の実体
- 値: 変数に紐付いているデータ
- 時間: 再代入のたびに生じるアイデンティティと値の紐付き関係の遷移
;; Clojure
(defn heavy-calculation [xs]
(let [result (volatile! 0)] ; 初期値を指定して参照型を導入
(doseq [x xs]
(vswap! result + (* x x))) ; 参照型の値を更新(差し替え)
@result)) ; 参照型の値を参照- 参照型では構造的に分離されていて simple
- アイデンティティ: 参照型そのもの
- 値: 参照型が保持するデータ
- 時間: 更新操作のたびに生じるアイデンティティと値の関係の遷移
⚠️ 並行処理向けにはatom(CAS),ref(STM), etc.- ℹ️ Scalaのサードパーティライブラリには
Refなど参照型を提供するものがある
// Scala: (継承を備えている)オブジェクト指向言語の例
// インターフェースの定義
trait Shape:
def area: Double
// クラス(型)の定義とともにインターフェースの実装
class Rectangle(width: Double, height: Double) extends Shape:
def area = width * height
class Circle(radius: Double) extends Shape:
def area = scala.math.Pi * radius * radius- サブタイピング(サブタイプ多相)により、継承関係にある複数の型/クラス同士が complect している
;; Clojure
;; インターフェースの定義
(defprotocol Shape
(area [this]))
;; データ型の定義
(defrecord Rectangle [width height])
(defrecord Circle [radius])
;; インターフェースの実装
(extend-protocol Shape
Rectangle
(area [{:keys [width height]}] (* width height))
Circle
(area [{:keys [radius]}] (* Math/PI radius radius)))- Clojureのプロトコルではデータ型定義とインターフェース実装が分離可能で simple
- Expression Problemへの解答例でもある
- ℹ️ Scalaにも型クラスというアドホック多相機構がある
# Elixir: パターンマッチングを多用する言語の例
defmodule Geometry do
# 関数定義とともに引数でパターンマッチ
def area(%{type: :rectangle, width: w, height: h}),
do: w * h
def area(%{type: :circle, radius: r}),
do: :math.pi() * r * r
end- パターンマッチングではパターン(who)と対応する振る舞い(what)のペアが複数 complect している
;; Clojure
;; ディスパッチ関数
(defmulti area :type)
;; ディスパッチする値ごとのメソッド実装
(defmethod area :rectangle [{:keys [width height]}]
(* width height))
(defmethod area :circle [{:keys [radius]}]
(* Math/PI radius radius))# Elixir: 並行処理機構にアクターモデルを採用する言語の例
# メッセージを受信するプロセス
pid = spawn(fn ->
receive do
msg -> IO.puts(msg)
end
end)
# プロセスへメッセージを送信
send(pid, "hello")- アクターではメッセージに対する振る舞い(what)とメッセージを送受信するプロセス(who)が complect している
;; Clojure
(require '[clojure.core.async :refer [<! >! chan go]])
;; チャネル/ブロッキングキュー
(def ch (chan))
;; 軽量スレッドでチャネルへ値を送信
(go (>! ch "hello"))
;; 軽量スレッドでチャネルから値を受信
(go (println (<! ch)))- core.asyncでは軽量スレッドでの振る舞い(what)と値を送受信するチャネル(who)が分かれていて simple
-
clojure.spec: データ仕様を述語で記述する一種のコントラクトシステム
-
metosin/malli: データ駆動のスキーマライブラリ
-
cf. Typed Clojure: Clojureに型システムを追加するライブラリ
-
🐬< データとスキーマを分けて扱うアプローチが simple
-
Clojure CLI:
deps.ednの中身はただのデータ -
tools.build:
build.cljの中身はただの関数 -
cf. Leiningen:
project.cljの中身は(マクロを含む)コード- JavaのMavenの系譜のビルドツール
- プラグイン拡張モデル
-
🐬< 設定(データ)とビルドロジック(関数)が分かれていて simple
-
Clojureエコシステム:
- 言語本体: 2009年(v1.0)から2026年(v1.12)現在に至るまで破壊的変更のない積み重ね
- 標準/サードパーティライブラリ: 同様の安定進化
-
cf. Scalaエコシステム: 破壊を伴うイノベーション
-
🐬< simple な構造を吟味しながら組み合わせて破壊なく進化させていく文化
- A History of Clojure (HOPL IV, 2020)で示される、ClojureとScalaのコードベースの進化の違い
- 🐬< Rich Hickeyも述べるように良し悪しは一概に言えないが、コミュニティの価値観が表れている
-
"Simple Made Easy"の核心: simple ≠ easy
-
simple である(simplicity)
- 設計対象の構造の単純さ
- 客観的な概念
-
easy である(ease)
- 利用者からの(広義の)近さ
- 主観的/相対的な概念
⇒ Clojureエコシステム/コミュニティは simplicity を追求して長期にわたり安定進化してきた
-
simplicity → 対象/客体(object)・モノの構造
-
ease → 主体(subject)・ヒトの対象との関わり方
- 学習/経験や環境など、個人/社会の対象との向き合い方
-
simplicity と ease を区別し simplicity を優先する →
- モノへの投資とヒトへの投資を区別する
- ヒトが移り変わっても残るモノには最初に意識的に投資する
ars longa, vita brevis
-
🇬🇧: Art is long, life is short/brief.
-
🇯🇵: 技芸は長く、人生は短い。
-
古希語(原典): ὁ βίος βραχύς, ἡ δὲ τέχνη μακρή
-
原文での趣旨: 人生は短いのに対して(医療)技術の道は長い(だから時間を無駄にせず学ばなければ)。
-
後世の意味の変化: (芸術家の)人生は短く儚いが、芸術は長く残る。
-
言語/エコシステムとコミュニティの選択
- Clojure: simplicity を追求しながら ease を高める
- 他のLisp (e.g. Common Lisp)
- 他のJVM言語(e.g. Scala)
- 他の関数型言語(e.g. Scala, Elixir)
-
ソフトウェア開発者として、さらには人として
- いかなる技芸を学び磨き上げ、何を残せるだろう
- 自分と社会のより良い体験/人生に向けて、何ができるだろう
-
"Simple Made Easy"を一通り観てみる
-
Clojureの背景を探ってみる: A History of Clojure
-
Clojureエコシステム/コミュニティに触れてみる
user> (-> (simple-made-easy-made-easy)
revisit
deepen
extend)
"Simple Made Easy" Made Easier
;; simplicityという長く続く価値を、皆さんにとってより身近に

