いわゆる純粋関数型プログラミング言語は、「状態や作用を扱うことはできない/難しい/面倒」といわれることもあります。でも、たとえばJavaScriptでコンソールにHelloを出力するには、
console.log("Hello")
と書く一方で、純粋関数型言語のひとつであるPureScriptでは1
log "Hello"
と書けます。また、たとえばJavaScriptで変数fooに42という値を再代入するには、
foo = 42
と書きますが、PureScriptで同じようなことをするには、
write 42 foo
のように書けます23。具体的なコードで比較すればわかるとおり、純粋関数型言語で状態や作用の扱いが難しいとか面倒だというのは誤解と言っていいのではないかと思います。
ファイル読み書きやネットワーク通信も普通にできます。たとえばJavaScriptでHTTPリクエストを送るには
let response = await fetch("https://jsonplaceholder.typicode.com/posts")
とfetchを呼びますが、PureScriptではgetを使って
response <- get string "https://jsonplaceholder.typicode.com/posts"
と書けます4。「純粋関数型言語はすべての関数に副作用がないので、作用や状態変化を扱えず、ファイル読み書きやネットワーク通信などは一切できない。電卓くらいにしかならず、実用的なプログラムは作れない」という旨のコメントもたまに見かけるのですが、ぜんぜんそんなことはありません。
というわけで、純粋関数型言語の「変数はすべて不変」「関数はすべて純粋で副作用がない」という噂を聞いて、どんなとんでもないコードになるのかと思った人もいると思いますが、実際はJavaScriptとも似たようなぜんぜん平易なコードで書けます。関数型は普通のやつです。
そのあたりがわかってくると、別の疑問が湧いてくるかもしれません。
- 結局は普通の言語と同じようなコードになるのなら、なぜすべての関数を純粋にする必要があるんだ?どんなメリットがあるんだ?
- 簡単なサンプルコードではJavaScriptと似ていても、現実のプロダクトで複雑で大規模な作用を扱うコードだと、JavaScriptとは似ても似つかないコードになるのでは?
- そもそも、
log "Hello"でコンソールに出力されるのなら、log関数には副作用があるじゃねえか。「どんな関数にも副作用がない」というのは嘘じゃねえか? - もし本当に副作用のない純粋な関数のみなのであれば、副作用のない関数をいくら組み合わせても副作用のあるプログラムにはならないはず。本当は実用的なプログラムは作れないのでは?
-
write 42 fooで変数fooに再代入できるのなら、fooは可変じゃねえか。ぜんぶ定数で不変ってのは嘘じゃねえか? - 状態や作用といっても他にもいろんな種類がある。GUIや画像操作、オーディオ操作、乱数、日付時刻もあるし、それらすべてが本当にJavaScriptと同じように書けるのか?5
- 例のモナドとかいう謎のアレはどこに出てくるんだ?どこにもそんな気配がないぞ?
これらの疑問の答えはこの記事には書ききれないので、他の記事を参考にしたり、ぜひご自分で何らかの純粋関数型言語に触れて確かめてみてください。LLMに訊いてみるのもいいですね6。
-
筆者の好みや、JavaScriptとの対比がしやすいという理由で、ここではPureScriptのコードを例にだしています。純粋関数型言語で一番メジャーなHaskellでも、関数の名前が異なる(
print)くらいで、似たようなコードになります。わかりやすさを重視してタイトルでは「純粋関数型言語」と主語をデカくしてしまいました。すみません。同じ純粋関数型言語の一族でも、Elmのような言語はまったく別の書き方になります。 ↩ -
「変数の再代入」みたいな振る舞いには
writeを使う書き方になりますが、定数を定義するときはfoo = 42と書けます。 ↩ -
何か語順が慣れないとか、
writeという関数呼び出しが気にくわないということであれば、writeの別名として:=のような演算子を定義して、foo := 42と書くとオシャレかもしれません。 ↩ -
getの最初の引数のstringは、結果の型を指定しているだけです。JavaScriptのfetchだと、そのあとで.json()や.text()を呼んでそれぞれの型に変換しますが、それをgetでは事前に指定しているだけですね。本質的な複雑さに大きな違いはないと思います。 ↩ -
実は状態の読み出しの部分はJavaScriptとかなり異なるコードになります。リンク先のプレイグラウンドのコードを見ると、なんやコレ!? となると思います。JavaScriptより面倒かどうかは……まあ気持ちや慣れによります。それに現実のコードでは、この
writeやreadはあまり使う必要がありません。 ↩ -
ちなみに、この記事は生成AIを一切使わず、すべて人間の手で書かれました。サンプルコードも人間の手で丹精込めて書かれた温かみのあるコードです。 ↩