OCamlでは強い型付けがされているため、int型とfloat型のあいだを簡単に行ったり来たりすることができません。たとえば
# 1 + 2;; - : int = 3 # 1.3 +. 2.4;; - : float = 3.7
という具合にint型にはfloat型とは別の演算子を使う必要があります。この点が僕が「この言語は制約が強すぎて使いにくそうだ」と思った部分でした。
ところがマニュアルを読むと、可変型(variable type)を使って自動的に型変換をする方法が載っていたのです。以下プログラムはOCamlのリファレンス・マニュアルから抜粋。
#type number = Int of int | Float of float | Error;; type number = Int of int | Float of float | Error #type sign = Positive | Negative;; type sign = Positive | Negative #let sign_int n = if n >= 0 then Positive else Negative;; val sign_int : int -> sign =#let add_num n1 n2 = match (n1, n2) with (Int i1, Int i2) -> (* Check for overflow of integer addition *) if sign_int i1 = sign_int i2 && sign_int(i1 + i2) <> sign_int i1 then Float(float i1 +. float i2) else Int(i1 + i2) | (Int i1, Float f2) -> Float(float i1 +. f2) | (Float f1, Int i2) -> Float(f1 +. float i2) | (Float f1, Float f2) -> Float(f1 +. f2) | (Error, _) -> Error | (_, Error) -> Error;; val add_num : number -> number -> number = #add_num (Int 123) (Float 3.14159);; - : number = Float 126.14159
number型はint型のInt、float型のFloat、あるいは型指定なしのErrorという値になることができます。つまり、number型の変数には型情報と値情報が含まれるわけです。add_num関数ではnumber型の変数の型情報に基づいて場合分け(パターン・マッチング)をし、必要であればキャスティングをしてから計算を行います。(「変数」と言うのは語弊があるかも。メモリ内容を変化させることができるという意味での「変数」ではなく、代入のたびに新しいメモリ空間が割り当てられるようになっています。ただ配列などの例外もありますし、mutable指定することによってメモリ上書き代入ができます。)
見た目はLISPのほうが好きですが、OCamlのほうが自分の性に合っているような気がしてきました。ただ、ひとつのリストに様々な型の値を放り込んだりできなさそうなのと、多次元配列がなさそうなのが痛いかも。そこらへんはもっと勉強すれば簡単に解決できるのかもしれないけど。・・・十分にLISPっぽい機能を備えたPythonという手もありますなぁ。