月: 2021年3月

F#でray tracing

先日F#に入門してみた。F#は.NET環境で動作する関数型言語で、OCamlの方言程度のものといった雑な認識だったんだけど、入門してみてその認識は改められた。OCamlとはぜんぜん違う、ML系の別個の言語だといっていい。

F#はML系列の言語だが、独自の構文規則があって、とくにインデントに意味をもたせているところがユニーク。これによって余計なセミコロンなどがかなり省略できるのは面白い。let … in 構文みたいなのもinなしで書かせられるし、またF#はOCamlと同じで、リストの区切りがセミコロンになっている([1; 2; 3] のように書く。[1, 2, 3] は [(1, 2, 3)] と同じと解釈され、タプル1つのリストとなる)が、リストの各要素で改行すれば要素区切りのセミコロンすら省略できる。

F#は.NETに標準添付されていて、特別なインストールを必要としないというのも面白い。.NETをインストールすれば インストールは完了している。dotnetコマンドでdotnet fsiを打てばreplも立ち上がる。

さて、入門といってチュートリアルドキュメントを適当にいくつか眺めただけではちょっと面白くない。練習がてらなんかしらのプログラムを書いてみたいところだけど、とくに書きたいプログラムのアテもない、というわけでRay tracing in a weekendというやつをF#でやってみた。もとのコードサンプルはC++で書かれているが、これをF#で書いてくことの面白さみたいなものもあるだろう。たぶん。

速度を出すことは目標ではないので遅いだろうし、実装もしょぼいとは思うが、書いたものは想定通りに動いて、the next weekまで修了したので目標を達成できたとおもう。じつはray tracing実装は初めてだったので、そこもまぁ良かった。

で、書いてみることでいろんな言語機能を使ってみたりして楽しく、F#の良さも楽しめたと思う。F#はOCamlと違ってオペレータのオーバロードもできるので、ベクトル同士の足し算とかを + で書けるように定義できたりしてラクだったりした。

一方書いていてこれはいまいちだなあと思ったところもいくつかあった。最大のものは、意外とオブジェクトが相手だと型推論がちゃんとしてくれないこと。具体的には、たとえば乱数オブジェクトから乱数を引いてランダムな3次元ベクトルをつくる関数、

let random_vec rnd = Vec3(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble())

みたいなのを書いてみたとすると、

error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.

みたいなエラーが出てくる。適当に型推論してくれることはなく、オブジェクトを引数に取る場合はかならず型を指定しないといけない。

let random_vec (rnd: Random) = new Vec3(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble())

これはちょっといけてない。レコード型などほかの型なら推論してくれるんだけど……。ちなみにOCamlはこういうのの型推論もよろしくやってくれる(NextDoubleというメソッドを持つオブジェクトなら良い、みたいに推論してくれる)。これは言語そのものというより、バックエンドである.NETのオブジェクトの構造を反映しているからだろう。ほかにもインタフェースにキャストしてあげたりといった面倒が意外とあった。

まぁでも久々に関数型言語で遊べてそれなりに楽しかったな。また何か使う機会をうかがいたいところ。