OCaml を学んでみて

最近技術書をまとめ買いしまして、ついでに「プログラミング in OCaml」も購入しました。OCaml自体、Haskell学習中の身としても興味がありましたので、こりゃちょうどいいやということで。

プログラミング in OCaml ~関数型プログラミングの基礎からGUI構築まで~

プログラミング in OCaml ~関数型プログラミングの基礎からGUI構築まで~

プログラミング in OCamlですが、発行年を見ると2007年・・・。実に5年前です。5年前というと卒業して就職したあたりです。その頃は・・・何に興味があったんだろうなぁ。まぁそれは置いておいて、全部読んでみたんですが、この本は元々大学で使われていたテキストをベースにして書かれているとされていましたが、いかんせん私は大学に行っていないヘタレですので、わりかし読むのに苦労しました。

苦労したというのは、書き方とかじゃなくて、内容を理解するのに手間取った、ということです。書き方自体はテキストということもあり、かなり読みやすく書かれていたかと思います。ただ、確か利用されている大学のレベルが高いためか、頭の回転が鈍い私は理解できるまで?マークが大量に飛び交う部分が多かったのは白状しておきます。

内容自体は、OCamlの基礎となる部分がほぼ網羅されているようで、多相Variantなども解説されていました(私はこの辺りになると理解しきれませんでした・・・)。OCamlも3.12から色々新しい要素が追加されたと聞いていますが、それでも基礎となる部分は変わらないでしょう。

読むと同時に、GUI周りを除く大体の練習問題もやってみましたが、Newton-Rapthon法が突然出てきてびっくりしました。当方、ゆとり前夜世代のため、高校レベルではこんなん学習してませんでしたので、かなり苦戦しました。というか、全体的に練習問題のレベルが高く、一問一問に時間がかかりました。その分理解は深まったと思いますが。

OCamlについて思ったこと

OCamlの基礎中の基礎レベル(多分)を学習してみて思ったのは、まず多くの方が言われるように、シンタックスが独特だなぁと思いました。後、Haskellをやってからだと型のつけ方でありゃ?というのをがわりかし多くやらかしました。

特にリスト・配列の区切りとタプルの区切りが区別されているのは、練習問題レベルではなかなか慣れきらんでした。どうしてもカンマを入力してしまいます。それと、tuareg-modeのハイライトでもあるんでしょうが、キーワードがHaskellと比較してかなり多いので、見た目が非常にカラフルです。

OCamlHaskellで一番異なる?のは、デフォルトの評価が正格か遅延か、という点のようです。OCamlはeagerな、Haskellはlazyだとはよく聞きますが、それを実感したのが関数定義でした。

Haskellでは、最終的に定義されてさえいればいいので、

foo = print $ bar
bar = "foobar"

みたいなのでも成り立ちますが、OCamlでやるとコンパイル時にエラーになります(確かめてませんが・・・)。この性質から、Haskellだと、whereの下にとりあえずずらずらと関数定義を書き並べて(順序はバラバラだったりする)ということがよくやられますが、OCamlだとletで定義する順番をちゃんと利用される順にしておかないとエラーになるので、Haskellのような手法は無理だと理解しています。

個人的には、確かにHaskellのwhere以下にずらずらと並べる形式は、書いてるときはまぁいいんですが、ちょっと経ってから見直した時に、えーとこっちでこれが呼ばれているから・・・となることを経験しています。なので最近はwhere以下に関数を並べるときも、できるだけ利用される順に書くようにしています。もしくはHaskell的ではないかもしれませんし、何か不都合が出てくるかもしれませんが、そこでだけ利用されるような値や関数はletを利用して定義するようにもしています。(これって多分なんか不都合があるからwhereを大量に利用するようにしているのかもしれませんが、その辺がいまいちわかっていません)

それと、OCamlでは再帰関数と通常の関数定義が明確に分かれている、というのも新鮮でした。Haskellだと関数は関数ですし。ただHaskellだと、これが再帰だ、というのは読んでみないとわかりませんが、OCamlだとlet recがあったらとりあえず再帰だ、と認識できる点は中々いいかも、と思いました。惜しむらくは大体再帰を利用したいような部分はfoldとかmapとかfilterで事足りる、という点でしょうか。よく言われる通り、通常再帰を頻用するような部分については高階関数を利用する、というやつです。

私が知っている関数型言語が、OCaml以外だとHaskellSchemeくらいしかないので比較するのもあれですが、個人的にはHaskellの遅延評価よりかはOCamlの正格評価の方がいいです。これまたよく言われる通り、遅延評価だとどういう順序で評価されるかがパッと見で理解できないため、なんで効率が悪いのかがよくわからないまま修正するイメージがあります。

むろん、遅延評価だからこその記述力の高さというのもありますし、常識的な使い方をしている限りは滅多なことでは問題にならない、というのは確かでしょうが、「計算量見積もりが強烈に難しい」というのが、実用レベルに至っている関数型言語の中に、デフォルトが遅延評価であるものがHaskellくらいしかない傍証になるのかなぁ、と思います。(他にあるのでしたら、それは私の不勉強です)

あと、OCamlのモジュール機構はかなり強力だということはよくわかりました。Haskellのモジュール機構は貧弱なことで有名?らしいですね。
個人的には型クラスのメソッドを強制的にトップレベルの名前空間にぶちまけるのは勘弁して欲しい気がします。

まとめ

なんというか、久しぶりに新しい言語を学んだ気がしました。しましたというか実際にそうなんですが。新しい言語を学ぶと、その言語における考え方とかもまとめて学べる、というのは実感できました。

私は学ぶだけなら、両手くらいの数は学んだとは思いますが、それから趣味でもなんでも、実際に使ったことがある言語はかなり少ないです。ここでの「使った」の定義は、「仕事で使ったか、もしくは何かアプリを作ったか」としておきます。ざっと思い返すと、

  • COBOL
    • 仕事で仕方なく・・・。早く絶滅してほしい言語ですね。
  • Java
    • 仕事で。まだまだ現役の言語ですね。
  • C++
    • 学生時代の卒業作成でそこそこガッツリと。Boostとかは使ったことありませんが、TMPがもてはやされてた時期だった気がします。

HaskellとかRubyPythonも趣味レベルでは書いていますが、いかんせん私はどうにも動的型付系統の言語が苦手なようです。ちょっと規模が大きくなった瞬間に頭がパーンとなります。その辺も、静的型付であるHaskellとかOCamlが何となく肌に合う理由なんでしょうか。

まー次の仕事もJavaで、今回OCamlを学んだのもあくまで自分の好奇心でしかないわけですが・・・。いろいろな知識を仕入れるのも大事だよね、ということでまとめます。