OCamlのifは文でなく式である。あまり覚えてないが確かHaskelもそうで、関数型言語の特徴といえる。文と式で何が違うかというと、式だとif云々の結果の値が必ず存在するということにある。動きはC/C++/Javaでの三項演算子に近い。
https://v2.ocaml.org/learn/tutorials/if_statements_loops_and_recursion.ja.html
Goには三項演算子はないそうだ。
https://asapoon.com/article/golang-post/4452/golang-ternary-operator/
三項演算子の乱用による読みにくいコードを書かれるのを防ぐためのGo設計者の選択だそうだ。
C/C++/Javaは三項演算子の乱用が起きやすいのは経験上理解している。
さて、以下の先頭の文字を大文字にする関数のサンプルコードを見る、
(* OCaml *) let capitalize s = String.mapi (fun i c -> if i == 0 then Char.uppercase c else c ) s /* Go */ func capitalize(s string) string { r := []rune{} for i, c := range s { if i == 0 { c = unicode.ToUpper(c) } r = append(r, c) } return string(r) }
OCamlのifは式で、Char.uppercaseの戻り値は自動的にString.mapiに渡されている関数の戻り値になっている。Goのifは文で、unicode.ToUpper(c)の戻り値を明示的に変数で受け取らなければならない。また、関数型のmapような関数の考え方はないので、runeの配列に文字を追加するコードが必要になる。
OCamlのifは通常elseも必須で、無加工のcを明示的に返している。Goのifはelseは書いてない。c = c なんてのは普通は書かないからだ。OCamlでもif云々の結果の値を捨てたりとかelseなしにするとか変数への代入とか、結果的にGoと同じに動くようなコードも書けなくはないが、敢えてそういった構文を使うなり面倒な書き方になる。
さて、c = unicode.ToUpper(c)をunicode.ToUpper(c)と書き間違えたらどうなるか。Goのコンパイラは文句を付けない。これは典型的な代入忘れのバグになる。OCamlではコンパイルが通ったら概ね意図したコードが動くという感触を得るが、GoやC/C++/Javaではそうはいかない。代入忘れやelse忘れは結構バグの原因になってるのではなかろうか。
関数型言語に慣れると、if文の中で代入するのがもどかしく感じるようになる。
Goでifが式でなく文なのはGo設計者の選択なんだろうが、if式は人類にはまだ早いとでも考えたのだろうか。OCamlはコンパイルが通るようになるまで約束事が結構厳しく、特にプログラミング初心者には厳しい言語かもしれない。
2024.05.14
OSTRACISM CO.
OSTRA / Takeshi Yoneki