文脈: https://x.com/tomjaguarpaw/status/1917938120277872935
私の理解について話させてください…
この記事の定義に従うと、参照透過性と純粋性に大した違いはなく、わざわざ純粋という言葉を使う必要性は薄いというのは正しいと思います。
ただ、少なくとも私が見てきた議論では、純粋という言葉はこの記事の定義とは異なる意味で使われていると思います。指摘の通り、この記事は関数以外の、プログラム(あるコード断片)の純粋性について定義していませんし。
プログラムが純粋であるとは、それに副作用がないということです。これが、人々が使っているはずだと私が理解している、プログラムの純粋性の定義です。副作用とは、それが外界(言語「Haskell」が公式に定義する計算モデルの外部)の状態を変化させる、または外界の状態を受け取りそれに応じて以降の計算の進行を変化させるようなものです。参照透過性に由来する概念ではありません。ここで外界・内界の境界のどこに置くか、つまり「どこまでがHaskellなのか?」が問題になります1。まさにこれが問題なのです。
-
ある人は、
IO aで型付けられた値(特に、main :: IO ())を正規形 (normal form) へと簡約するものがHaskellであり、そのIO ()型のNF項をどうしようと外界の勝手で、Haskellの関知することではない、故にすべてのHaskellプログラムには副作用は一切ないと言います。 -
ある人は、そのNF項を機械語に翻訳してCPUで実行することまでがHaskellである、故にprintするHaskellプログラムには副作用があると言います。
-
ある人は、宇宙全体がHaskell計算機で、宇宙の状態は
World型で、この宇宙の時間発展はState Worldモナドの値をNFへと簡約していくステップである、故に外界は存在せず、すなわち外界の状態とは()型であるので、外部状態が変化することはない、故にすべてのHaskellプログラムには副作用はないと言います。
個人的な見解を述べると…
まず3.の立場は少し哲学的ですが、とはいえ、この立場で話を進めている文献もあるので、この見方が役に立つ場面は必ずあるはずです。
それ以外の2つの立場については、これらは一見、単にそれぞれHaskellのことを「Haskell言語」/「Haskell処理系」と言うだけで問題は解決するように思えます。 しかし厄介なのは、処理系も含めて「言語」と呼ぶことがあるということです。 コンピュータサイエンスでは通常、(プログラミング)言語と言えば、それは構文論+意味論を指すにも関わらずです!2 これが、議論を紛糾させている問題の最も深い根源だと思います。CS的な立場から見ると、これは用語の濫用に思えます。
「構文論+意味論」の方を指す言葉を新しく作れば問題は解決でしょうか?少なくとも紛糾は避けられそうです。おそらく「仕様」と呼ぶのが良いと思います。 「Haskell仕様(Haskell2010 language reportなど)におけるHaskellプログラムには副作用は存在しない(純粋である)」「Haskell処理系(例えばGHC)におけるHaskellプログラムには副作用は存在する」、これが最も実情に合っているはずです。
関連する質問項目(意味はないかもしれません):
- Cと言った時、思い浮かべたのは C99 のようなものですか?それとも
gccやclangですか? - Rustと言った時、思い浮かべたのは Rust仕様 ですか?それとも、
cargoコマンドですか?
あるいは、それらが成すエコシステムの総体かもしれません。むしろそのような人が多いかも。
ともあれ、これはとにかく言葉の使い方の問題であり、不毛なことです。人々が定義を共有し、円滑に議論できることを願います。
最後に、Haskellコードが純粋であると考える(すなわちHaskell仕様でものを考える)ことのメリットについてですが、これは単に「仕様」側の見方をしている人々と議論を円滑に交わしたり、彼らの書いた文献を円滑に理解することができるということに尽きると思います。一般論として、複数の見方を時と場合に応じて使い分けることができるのが、最も理想的な状態だと思います。とはいえ、教育の観点において、初学者にすべての見方を叩き込むのはあまりにハードなので、まずはどれか一つ、親しみやすいものから始めるというのはよい考えです。
まだプログラミングをやったことがない人にとっては、もしその人が算数の初等教育を完了しているのであれば、「仕様」側の教え方のほうが簡単に感じるはずです。 なぜなら、算数もHaskell仕様も、本質的なメンタルモデルは規則に従った記号の書き換えだからです。 一方で大半のケース、すなわち既に他の言語の経験がある人に対しては「処理系」側で教えるのがよいでしょう。
いずれにせよ、複数の見方ができるということも同時に教えておくことが、その人の将来のためになると思います。
Footnotes
-
↩Programming languages are described in terms of their syntax (form) and semantics (meaning), usually defined by a formal language. https://en.wikipedia.org/wiki/Programming_language
例えば
main = print "Hello"これに対するそれぞれの立場からの応答は:
これは純粋なプログラムです。mainの右辺はこれ以上簡約できない項であり、
x = Just "hello"のような定数の定義と全く変わりません。単に型がMaybe StringではなくIO ()なだけです。既にmainは簡約されきっているので、Haskellがすることは何もありません。Haskellとは簡約器、つまり項を入力として簡約後の項を出力する数学的な関数(写像)であり、この場合は入力がprint "Hello"で、出力はprint "Hello"です。これは非純粋なプログラムです。printは外界にあるディスプレイの状態を書き換えます。より現実的には、RAM内のttyの出力のバッファへの書き込みです。
3つ目の立場については、実はあまり良く知らなくて、あまり変に想像を働かせるのも良くないので書かないことにします。どなたかこの立場について知っていたら説明をお願いします。