OSTRACISM CO.

OCamlとGoとC++と...

if文とif式

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