
2026/04/29 17:43
なぜ私はハスクェルではなく、リプスまたはスキームを選び続けるのか\n\n関数型プログラミングは多くの開発者を魅了しています。\nハスクェルの型システムは設計段階から正しさを保証します。\nしかし、実際の利用において難易度を高める側面もあります:\n- モナドの過剰な使用が記述を冗長かつ扱いにくくすることになります。\n- 厳密評価(strict evaluation)はインタラクティブ開発のニーズと対立することがあります。\n一方、リプスやスキームはより柔軟な体験を提供します:\n- マクロシステムにより、表現力を損なうことなく言語拡張が可能です。\n- 動的型付けを支持する一方で、スクリプティングから大規模プロジェクトまで強力に機能します。\n究極的には、選択はあなたの具体的な目標と好みに左右されます。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
原文サマリーは、すでにすべての主要ポイントを明確かつ簡潔にカバーしています。
Text to translate:
The original Summary already covers all key points clearly and concisely.
本文
ソフトウェア工学において、数学的に純粋で美しい「プログラムという理想」の間と、手っ取り早く成果を出すための泥臭く実用的な現実との間に、長く続く緊張関係が存在しています。私のキャリアを通じて、私は hacking のために最適な個人の「スウィートスポット(心地よい領域)」を見つけようとして、両極端の側面を深く探究してきました。
キーボードを研ぎ澄まし、タイトルを巡って熾烈な議論を始めようとされる前に付け加えますが、本稿は Haskell やその他のツールの悪口を書いたものではないことを明かにしておきます。むしろ、私は Haskell を愛しています。独学で挫折し壁に頭を打ち付けた期間も三年間にも及び、実際に複数の生産的なプロジェクトを構築しました(中にはいくらか収益を上げたものさえあります)。
Web 開発の世界、Go の世界、Java/Scala/Kotlin といった JVM 系技術との経験、そして Emacs や Common Lisp・Scheme における Lisp での長年の hacking の履歴を通じて、私は関数型プログラミングを深く評価するようになりました。
明快な洞察 #
- Haskell(より ML 系の言語も)は、おそらく作業対象として驚くほど素晴らしい、啓発的でかつ複雑な型システムを提供します。
- また、Haskell は数学的な概念やアイデアをプログラミングに導入し、普及させる分野において疑いなしの王者です。Haskell のコミュニティには博士号保持者やコンピュータサイエンス研究者、圏論家のような知的な人材が多く集まり、その他さまざまなコミュニティ(Schemers 集団なども)を見下すべきではありません。
- Haskell が持っていたり、それを普及に寄与したりした驚くべき革新の数々(私が幾度も頭を吹き飛ばされたものです):
- 代数型(Algebraic Data Types)
- パターンマッチング
- フункタとモナド
- モノイド、半モノイド
- モナドを用いてモデル化された効果を伴う計算
- 純粋な関数型ドメイン特異言語(DSL)など
- これらすべての要素は、他の多くの言語においては単独では付け足されたようなものを感じさせたり、あるいは完全に欠落していたりするものです。
Haskell が持つ驚異的な知性にもかかわらず、この言語は人々が素早く useful なコードを書くための試みの大半には抗います。特に関数型プログラミングに新しい方々にとって(神が禁ずるなら、モナドやフンクタにも!「モナドとは終末圏のモノイドであるに過ぎない」などと言っているが問題は何か?)といった場合にその傾向は顕著です。
実用主義による実際の生産性の促進 #
Scheme(そして一般的に Lisp)は Haskell の革新性や純粋性を欠いているかもしれませんが、むしろ最小限の柔軟性を優先しつつ、実用性と関数的美しさを融合させ、人間が使用する関数型言語として最適化されています。私の見解では、Scheme(および Lisp)を用いることで、他のいかなる言語よりもシンプルな用語で複雑なシステムや問題領域を表現できます。
例えば最近の私の冒険についてご紹介します。数年間のプロジェクトの中で生み出した多数の一つであるブックマーク管理ツールのプロトタイプを開始したのですが、それは Haskell で開始しました。データモデリングの美しさや副作用のない純粋な推論が効果的だと考えていたからです。また処理は高速でエレガントであり、Parsec や Servant、optparse-applicative などのモジュールを使いこなせば、パーサのような特定のものをそれなしでは想像し難くさえなります。
概念検証の一つステップとして、いくつかのデータモデルを XML に変換してファイルに出力する作業がありました。これを Kotlin や Java で行うなら、Gradle に依存関係を記述し、Jackson や標準の DOM パーサを取り付けるだけで十分で、たった 10 分後にデータはメモリ上にあり操作可能です。
しかし Haskell プロジェクトでは挫絶感に満ちた 1 時間の作業を過ごし、言語に三年以上も携わっている私ですらなおかつ依存関係やモナド API と格闘し、やがて私がそもそも何を目指しているのかさえ忘れたことに気づいたため、全体をあきらめました。
Haskell とのこの摩擦点は頻繁に起こりました。美しさは備わっていますが、設計を大前提にした上で手を泥臭くつけたいと願う際、あるいは型駆動開発も場合によっては良いものではありますが、事前に大きく設計せずに迅速にプロトタイピングしようとすると、Haskell はあなたに対して戦います。
Scheme(私の場合は GNU Guile)には Haskell のような劇的に効率的なコンパイラーはありませんが、C 言語の基盤のおかげで比較的速いです。何より重視すべきは、実際の hacking という行為そのものを喜びに変える点にあります。
Haskell の純粋関数型という基礎がどれほどエレガントであっても、ファイルへの書き込みやネットワーク通信といった単純で重要な不純なタスクを複雑化させます。この問題に対する Haskell の答えがモナドですが、それはしばしば「重厚な抽象化の税金」のように感じられます。有用なソフトウェアを書くことを可能にしますが、直感的ではなくプロトタイピングも遅くします。
このような力ずくの抽象化は、私の見解では確かに美しいのですが、多くのプロジェクトにとっては正当化できません。以下をよく考えてみてください。「本当に関数型効果システムが必要なのか?その複雑さと認知的負荷に見合う価値があるのか?純粋・不純な計算のコンパイル時の厳格さを本当に必要としているか?」そして覚えておいてください、後で単にどこかに
print を追加するだけでは動作せず、再設計が必要になる点(IO モナドへのようこそ)。
長年の Lisp 崇拝者として、私にとってこれは使いやすさに対する巨大な障壁です。多くの意味において、観察可能なことしか修正できません。Scheme は academic な純粋性を喜んで犠牲にすることで、
(write ...) をコードのどこでも叩き込んで瞬時に変化を確認できることを許容します。 Haskell 崇拝者の方はおそらく今まさに顔を両手で覆いながら Debug.Trace を挙げて「なぜ怠慢で最適化された言語に副作用を望むのか」と疑問を持たせようとしているでしょう。彼らは技術的には誤りではありませんが、素早い泥臭いデバッグ時に追加される摩擦は、私がスピードを重視する際の支払うことを厭う税金です。
メタプログラミングと DSL #
二つ目の問題は、モナドの最大の強さと直結しており、それは Domain Specific Languages(DSL)とほぼ同義であるという点にあります。DSL の約束事とは信じられるべきものです:問題を解くための複雑なプログラムを書き込むのではなく、そのタスクに特化して設計された専用言語でシンプルなプログラムを書くことです。Parsec はまさにこの黄金の児です;パーシング関数は事実上 BNF 文法そのものです。
しかし Parsec の成功は Hackage に数百家の数々の DSL を埋め尽くしました:パーサー用、XML 用、PDF 生成用など、すべてに一つずつ。各々は完全に異なり、それぞれ独自の学習曲線を要求します。Web API から JSON を読み取り XML を変異させ PDF に出力するような場合を考えてください。Java エコシステムではある程度の統一性を期待しますが、3 つのライブラリを取り込みたとしても、一般的には慣れ親しんだオブジェクト指向あるいは軽度な関数型規約に従います。しかし Haskell では、異なる三つのタスクのための DSL がそれぞれ独立しており、著者たちは厳密にドメイン最適化されて文法的一致性を完全に無視しています。JavaDocs を 5 分程度ざっと読む代わりに、DSL のドキュメントやチュートリアルで数時間過ごすことになります。
私たちが Schemers として知っている通り、Scheme は意図的にシンプルに設計されています。その単純さが制限ではなく、むしろ無限の柔軟性をもたらすものです。現代の JVM 系言語はこれを達成するために反射や複雑なコンパイラプラグイン(Kotlin の KSP など)に大きく依存していますが、Lisp ハッカーたちは強力なマクロシステムを用いて何十年も容易に言語を再形成し、自分の意のままに言語を広げ曲げてきました:
(define-syntax define-repo-method (syntax-rules () ((_ method-name accessor docstring) (define* (method-name repo . args) docstring (apply (accessor repo) args)))))
Haskell は Scala のような高度な型レベルプログラミングのように、同程度の柔軟性を実現するには多くの言語拡張を必要とすることが多く、Template Haskell やその強力だが恐ろしい API を用いることになります:
{-# LANGUAGE TemplateHaskell #-} import Control.Monad import Language.Haskell.TH curryN :: Int -> Q Exp curryN n = do f <- newName "f" xs <- replicateM n (newName "x") let args = map VarP (f:xs) ntup = TupE (map (Just . VarE) xs) return $ LamE args (AppE (VarE f) ntup)
Scheme を無数のプロジェクトで利用してきたのは、私にとっての「スウィートスポット」を導く特徴と哲学の組み合わせ 때문입니다。また進歩的な言語であり、未制約な革新(例:デリミテッドコンティニュエーション)を続けています。構文を直接自分の意思に合わせて成形したい場合、Scheme はあなたの邪魔になりませんし、実現を手伝います。
もちろん公平を期すためにToolkit 全体について言えば、標準的な Scheme はいわゆる大規模エンタープライズ生産のために JVM が提供する重量級の「バッテリーインクルード型エコシステム」を欠くことがあります。 Haskell と比較すると Lisp コンパイラは至多で modest でシンプルですが、それゆえに極めてアクセスしやすく(エラーメッセージもさらに親しみやすい)です。
私は Scheme が客観的に Haskell より優れていると述べているわけではありません。言語はツールであり、作業に適した適切なツールを選ぶべきです。 Haskell の関数的な美しさやアイデアから学んだすべてを忘れられませんでしたが、私にとって Haskell はプログラミング言語のプラトニック理想です:ある方向に道しるべとなる一方で、私が行なっていることの大部分には少々硬すぎるものです。
そして REPL:インタラクティブなワークフローと開発者の力 #
REPL(Read-Eval-Print Loop)とは、コンソールやランニングアプリケーション、言語コンパイラーなどとの接続を通じて利用可能なインタラクティブ環境であり、エンジニアとして超能力を与えてくれます。🦸🏼
特に Guile Scheme を含む Lisp 方言はこれに大きな支援を提供します。私自身ももちろん Guix、Emacs、(Arei/Ares + sesman)と組み合わせて究極の拡張可能な強力なエディタ体験を得ており、従来の IDE より遥かに進んでいます。🐂
いや、Python や Haskell (GHCIDE など)から知っている種類の REPL とは全く異なります。Lisp の REPL ははるかに多くを行なえ、あなたのエディタとシームレスに統合できます。評価、検証、変更、デバッグをライブで行いシームレスに。それは「編集→保存→コンパイル→実行」という低速なサイクルを排除することで開発ワークフローそのものを根本的に変化させます。全体プログラムを書き実行して何が起こるかを見るのではなく、迅速で対話的なワークフローを得られます。これは実践的に何を意味するのでしょうか?
- 段階的開発: 関数一つや一行ずつ書き込み、テスト、検査、評価し、アプリケーション全体を実行せずに即時のフィードバックを得ます。
- 強力なデバッグ: print 文を追加したり再起動したりすることを忘れます。オブジェクトを検査し、値を変更し、壊れた関数をその場でもう一度定義して修正をテストできるため、あらゆる環境(はい、稼働中の生産環境すら)で可能です。
- 高速なプロトタイピングと学習: 新しいライブラリや API を瞬時に試せます。ドキュメントを読むだけでは速くありませんから、読み込むだけで関数を呼び出して動作を確認します。
コードエディタに統合されれば、キーボードショートカットで任意のコード片(一行、選択範囲、またはファイル)を実行し、結果を瞬時に確認することができ、シームレスで強力な開発体験を生み出します。全体として、Lisp 言語は私にとってまさにスウィートスポットであり、私が考える良い開発者体験そのものです。これらには超能力も与えられ、永続できる美しいシステムを作成できます。