tmaeda 日記

トップ 追記

tmaeda 日記

興味の対象は、各種プログラミング言語(Ruby,Squeak Smalltalk, Objective-C,OCaml,など)、WebObjects, rails, Linux、Mac、数学、音楽、写真(特に VR)、猫などです。ツッコミは短く鋭く愛を込めて(出典:たださんの日記)。リンクはどうぞご自由に。

アンテナでの更新チェックには、antenna.lirs か、index.rdf をご利用ください。


2013-06-02 (Sun) [長年日記]

_ RubyKaigi 2013 に行ってきた

スタッフをしないRubyKaigiに参加したのは実に5年ぶりぐらい。 大変楽しませて頂きました。

一度スタッフをやってから一般参加者として参加すると、 スタッフの偉大さがものすごくよくわかった。 スタッフの皆様には感謝してもしきれないぐらい。 参加費もうちょっと高くてもいいぐらいです。

弁当とか飲み物配布とかすごく大変なんだろうと想像しますが、大変満足度が 高かったので可能なら続けてもらえると嬉しいです。 今回、公式の懇親会というのはなかったですが、弁当の時間に割とまったりと他のRubyistたちと コミュニケーションとれました。 3日間、同じ空間で一緒に飯を食うというのは一体感が生まれて良いですよ。

あと急なGithubドリンクアップも本当に良かったです。あれだけの人数を 無料で招待だなんて凄過ぎます。

同時通訳も事前の準備などすごく大変そうでしたが、 本当に評判が良かったみたいですね(ここは自分では判断できないので Twitterなどからの伝聞ですが)。

今まではマイクフォローなども各会場に2人ずつ配置して、質問があがったらすぐにそこまで マイクをもって走っていく、みたいな感じでしたが、今回は1人でマイクフォローをやっていたようです。 今までに比べると、質問者にマイクが到着するのに多少時間はかかってましたけど、その分 発表者の人が協力して前の方の席の人には発表者がマイクを渡す、みたいな場面がたまに見られて、 発表者と参加者が近くなるような雰囲気が生まれていて良かったと思います。

レポートも全セッションではなくて主要なもののみにしていたようですが、それで十分だと思います。 (動画の公開だってあるわけですし)

Tシャツやバッグの配布なども「ドサッと置いてあるから、各自持って行って」というゆるーいやり方で 大変良かったと思います。

というように、全体的に今までに比べてだいぶ力を抜いてゆったり運営している感じがすごく良かったと思います。 (それでもまだまだだいぶ大変なんだろうとは思いますが)

おかげさまで、帰って来てから日記を書く体力と精神力が残って、こうして日記を書けています。

では個人的に印象に残った発表に一言コメント。

割と concurrency の話が多かった気がしますが、個人的には concurrency 方面であまり困ってないので、 concurrency 成分は少ないです。

@kakutani さんのオープニング

英語での挨拶や司会、本当に大変だったと思います。素晴らしかったです。

@norio さんの Pixiv スポンサーアピール

プログラミング言語が国境を超えたコミュニケーションツールであり、 絵というのも同様だ、という素晴らしいメッセージでした。 世界中にユーザーが拡がっていて、各国語にサイトを翻訳しているのもすごいと思いました。

あとチャットとかコミックみたいなサービスもやっているのですね。知りませんでした。 (すみません、そっちの業界には疎くて...)

@_ko1 さんの Toward efficient Ruby 2.1

いつも通り、技術的に興味深い話をものすごくわかりやすく冗談を交えながらゆっくり話してくれて 非常に楽しめました。

@mirakui さんの High Performance Rails

Web+DB Pressなどでもよく記事を拝見していたので、初めて聞く話というのはそんなに無かったですが、 それでもいくつか忘れていたり、勘違いしていたりした箇所に気付けたので良かったです。

「このコードとこのコードどっちが早いと思います?」って会場に手を挙げさせて、 「実はこっちでしたー」ってやつが面白かったです。

あと、hamlは遅いけど、結局1回しかコンパイルされないから遅くても大きな問題にはならない、 みたいな話がすごく役に立ちました。

@eddiekao さんの Code Reading - Learning More about Ruby by Reading Ruby Source Code

昔札幌でも @mrkn 主催で Ruby のソースコードを読むって勉強会をやっていたので、 いろいろ共感しました。

プレゼンの内容も vim でどんどん tag jump してソースを読んでいくという内容で、 やったことない人には大変参考になる内容だったと思います。

Emacs ユーザー視点でも 「vim だとこんな感じなんだなー」というのがわかって 面白かったです。

プレゼンの後のランチのときに少し話させて頂いたのですが、ものすごく親切で 丁寧に教えてくれたり、話してくれたりして嬉しかったです。

RubyConf.tw に行きたくなりました。

@a_matsuda さんの Ruby on Your Rails

Railsの改良にいろいろな提案をして、採用されたり、リジェクトされたり、という話。 リジェクトされたものについても、個人的にはどれも素晴らしい改良だなぁ、と思いました。

merb っぽい引数の扱いとか、validationをブロックで書けるようにとか本当素晴らしいと 思います。

@nagachika さんのCRuby Committers Who's Who in 2013

これ本当に良い企画だなー。毎年連載して欲しいです。

コミッターとコミュニティが近くなる感じがします。

コミッターのコードレビュー(?)みたいな感じ。

Ruby Committers vs. the World

2011から(?)の連載物。これもコミッターの個性が見られたり、 コミッター間のやりとりが見えて、すごく好きだなー。 毎年やって欲しいです。

Friday hug

みんなで hug の格好で写真を取るという活動。こういうの楽しい。

@saturnflyer さんの If you do not enter the tiger's cave, you will not catch its cub: Objects, DCI, and Programming

私が今まで聞いた DCI 系の話の中で一番問題点とその解決策が整理されていてわかりやすかったです。

@nomadcoder さんのRapid development of enterprise web apps with Netzke

個人的には今回一番楽しみにしていた発表です。自分の業務に一番近そうな話でした。

Netzke は期待以上に素晴らしいフレームワークでした。ぜひ試してみたい。

@ktou さんの Be a library developer!

「想像するな、思い出せ」ってのが明確で良かったです。

@konstantinhaase さんの Beyond Ruby

Smalltalk とか Prolog の話w

NewSpeak って言語は初耳だったなぁ。

@mame さんの TRICK (Transcendental Ruby Imbroglio Contest for rubyKaigi)

今回の RubyKaigi で一番笑いました。本当に面白かった(全く役には立ちませんが)。

これに応募したり入賞できる人のことは心の底から尊敬します。

@_zzak さんの Contributing to Ruby

技術的に高度なことがわからなくても、 Ruby の開発には貢献できるんだよという話。

実際にどうやって作業しているかを丁寧に見せてくれました。 私にもできそうな気になりました。

@tanaka_akr さんの Fight with Diversity

3日間の RubyKaigi の最後にこの話かーw もう私の脳のライフはゼロですw

同時通訳の人もだいぶ辛そうな感じがしたw

聴衆に辛さと苦しさとそれを克服するという達成感を疑似体験させるという大変高度な 素晴らしいコンテンツでした。

聴衆の我々は1時間でもだいぶ辛かったのに、これを何ヶ月にも渡って克服したなんて、 この努力には本当に頭が下がります。

3日間を通して

普段に比べて、脳も体もだいぶ疲労していると思うのですが、なぜか夜は興奮して寝れなくなるんですよねー。 毎日寝不足でしたけど、それでも楽しめたので相当楽しかったのだと思います。

スタッフのみなさま、発表者のみなさま、スポンサーのみなさま、本当にありがとうございました。

_ RubyHiroba

何をやるのかよくわかってませんでしたが、ノリで参加登録して参加してきました。

LT大会とか、dRubyやRailsGirlsのハンズオンが行われていました。

だいぶゆるーい感じで、3日間の疲れた脳を癒すリハビリ場として大変良かったです。

最初は結構ちゃんと LT 聞いたりしていたのですが、午後からはほとんど通路で 他の Rubyist としゃべってました。

Rails Tutorial 翻訳プロジェクトに参加しよー、という誘いの LT があったので、 勢いで参加してしまいました。

ドリンク飲み放題ってのも本当にありがたかったし、お弁当が出たのも嬉しかったです。 Microsoft 様、Engine Yard 様、CodeIQ 様ありがとうございました。

あと、ゆるーい絵のステッカー貰えて嬉しかった。


2013-04-07 (Sun) [長年日記]

_ Express5800/110Gd から MicroServer に移行した

今までExpress5800/110Gd をいろいろ強化して使っていたのだが、これを更に強化するぐらいなら 新しいの買った方が良い気がしてきたので、 MicroServer に 乗り換えることにした。

Express5800/110Gd 強化版のスペック

  • Celeron 420 1.6GHz に換装
  • メモリ 4G に換装
  • HDD 1.5T + 0.5T に換装

MicroServer のスペック

  • N54L 2.2Ghz(2 core)
  • メモリ 8G に増設
  • HDD 110Gd のをそのまま引き継ぎ

さて、この MicroServer というやつは ここ に「低消費電力のAMD Turion™ II NEO を採用。コンパクトな筐体に加え、PC並みの静音と省電力を実現した、小規模オフィスでのご利用に最適なエントリーサーバーです。」と書いてある。

というわけで消費電力にどれぐらい差が出るのかを ワットメーター付きの電源タップで測ってみた。

ワットメーター付電源タップ

Express5800/110Gd

  • 電源をコンセントに挿しただけ → 3W
  • 起動中 → 70W ぐらいだが時折 100W
  • 通常時 → 70W
  • Rubyをコンパイルしている時 → 80Wだが時折85W

うーん、全く起動してなくて、電源をコンセントに挿しているだけの状態でも 3Wも食うのかぁ。これが待機電力というやつかぁ。

あと、電源を入れているときは Celeron 420 もだいぶ省電力な方だと思うのだが、 それでも常時70Wぐらいは食っている。

MicroServer

  • 電源をコンセントに挿しただけ → 0〜1W
  • 起動中 → 40W ぐらいだが、時折 50W
  • 通常 → 40W〜45W
  • Rubyをコンパイルしているとき → 45W〜50W

というわけで、CPUの性能は約3倍、メモリの量も2倍に増えているが、 消費電力は4割減ぐらいになっている。これは良い。理論上は電気代が月500円ぐらい 安くなるはず。

MicroServer はハードウェアの中身も非常に整理されていて、 コンパクトな割には HDD の換装なども比較的しやすいようになっている。 形はちょっと Next Cube に似ているけど、角が丸いので、あんまり Next cube っぽさは感じない。

静音性は Express5800 とそんなに大きくは変わらない気がする。

というわけで、そんなにパワーを必要としないけど電力消費を抑えたい人には MicroServer は大変良い選択肢だと思います。

OSはDebian(Wheezy)を使ってますが、firmware-linux-nonfreeというパッケージを入れないと、MicroServer の NIC が認識されなかったので、注意すること。

Express5800/110Gd

HP ProLiant MicroServer N54L (TurionII NEO N54L 2.2Ghz/2GB/250GB)


2013-03-20 (Wed) [長年日記]

_ Ruby勉強会@札幌 #24 で関数型言語の話をしてきました

背景画像から作り始めたら、案の定死亡フラグで全然間に合わなかった。

当日は島田さんに時間調整などして頂いて何とか発表できました。

いくつか不完全だった場所を直したり、発表中に受けたご指摘を 反映させたりした資料を公開しておきます。

プレゼン資料

あと、OCamlプレゼン用背景画像も作りましたので、 もし使いたい方がいらっしゃればご自由にどうぞ。

OCamlプレゼン背景用1

OCamlプレゼン背景用2


2013-01-01 (Tue) [長年日記]

日記というよりは年記というべき状態になっているヤバイ。Twitter, Facebook, Githubの大勝利ってことよね...

去年の目標の振り返り。

去年は本業が忙し過ぎたり、札幌Ruby会議2012の準備が忙しかったり、娘にいろいろ経験させるのに忙しかったりで、ほとんどそれ以外のことに手を出せなかった。

  • 何か作って公開&広く認知されるようにする -> 忙しかったので...
  • OCamlかHaskellでWeb+DBのHello World的なアプリ作ってみようかなぁ。 -> 一応サラッとYesodなどを触ってみた。
  • Ruby勉強会@札幌&Agile札幌皆勤 -> Rubyは皆勤した気がするけど、Agileは1回休んでるなぁ、たぶん。
  • TOEIC 860点以上 -> 810点で終わったorz。Readingのタイムトライアルが苦手。

その他。

  • 普段使いのRubyが1.9.2から1.9.3に変わった。Railsも3.1から3.2になった。あと、capybara(-webkit)とかFabricationとかの経験値が上がった。
  • 札幌Ruby会議2012でいろいろな外国の方と話せたり、後日ミハルさんと飲みに行けたのは良い思い出。
  • Scrum Boot Camp が非常に楽しかった。
  • Jenkins使うようになった。
  • Haxeを使いはじめた。
  • opamをはじめとして、OCaml周りが熱かった。
  • 圏論の面白さが少しだけわかった気がするので、ほそぼそと勉強している。
  • ソーシャルゲームというものを勉強のためにやってみた。よくできてるやつとそうでないやつとの差が激しい。よくできてるやつは、本当によくできてて感心する。
  • MacBook Air 11inch を買った。これは良い買い物だった。いつでも気軽に持ち運べる重さというのは素晴らしい。あと薄いので、キーボードを打つ手首への負担が少ないのも気に入っている。
  • iPod touch 4Gから5Gに乗り換えた。CPUが早くなったおかげでGoodReaderのレンダリングが早くなって読書が快適になったけど、明るさセンサー(?)がなくなったせいで、画面の明るさが自動調整されなくなったのは少々不便だ。カメラが非常に高性能になったのはありがたい。
  • 劇場版まどマギは大変良かった。009は時間が短過ぎて完全に消化不良。
  • 娘(5歳)が鉄棒で遊びまくって、逆上がり、空中逆上がり、空中前回り、足掛け回り、だるま回りなどなど今年1年で一気にいろいろできるようになった。もうすぐ蹴上がりもできそうな勢い。
  • 娘の教育も兼ねて久々に水泳をしてみたら50mも危ういぐらいだった(体力的に)。昔は軽く1000m以上連続で泳いでたんだけどなぁ。
  • そんなに大きな病気もなく健康だったと思う。でも最近寝ても寝ても眠いので、もしかしたら健康じゃないのかもしれない。なんとなく気が向いてアレルギーの検査をしてみたらハウスダストとダニに軽度のアレルギーがあることが判明した。鼻水が出るぐらいで大した支障はない。娘が重くなって来たので腰を痛めやすい。

今年の目標。

  • 何か作って公開&広く認知されるようにする
  • OCamlで普段使いのちょっとしたツールを作ってみる
  • TOEIC 860点以上

2012-10-28 (Sun) [長年日記]

_ OSXへOCamlの開発環境をOPAMで構築し直した

今年はOCamlの4.0系が出たり、opamというかなりイケてるパッケージ管理システムが出て来たりしたので、 OCamlの開発環境を作り直した。

typerex というのも今年出たので気になってるんだけど、私の環境では3.12.1ではビルドが通らないし、 4.00.1だとビルドは通るけど、emacsとサーバーが通信始めると例外が起きるので、様子見中。

というわけで、相変わらずtuaregなのであった。

前提とする環境は以下。

  • MacBook Air 11inch 2012
  • OSX 10.7.5 Lion
  • Xcode 4.5.1
  • GNU bash 3.2.48(1)
  • Emacs 23.3.1
    • anything.el 1.287(sprk2012でhelm.elを見て、そろそろhelm.elに移行してもいいのかなーと思ったけど、まだめんどくさくてやってない)
    • flymake
    • auto-install
    • init-loader.el
  • homebrew 0.9.3

手順は大まかには以下のようになる。

  • brew で ocaml 入れる(最低限でいいなら、これで完了)
  • (より便利にしたい人は)opam経由でocamlspot機能付きでocaml入れ直す
    • brew で opam 入れる
    • opam の設定
    • opam で annot パッチ付きの ocaml を入れる
    • ocamlspot 関係のパッケージを入れる
  • その他、便利なパッケージを入れる
    • utop
    • batteries
    • omakeを入れる
  • Emacsにtuaregとocamlspotを入れる
  • omake を試してみる
  • tuareg を試してみる
    • REPL(toplevel) の起動
    • 今書いているコードの一部を REPL で評価
    • その他
  • flymake を試してみる
  • ocamlspot を試してみる
  • ocamlbrowserを試してみる

ってな感じ。では、上から順番に行きます。

brew で ocaml 入れる

あまり説明の必要ないですね。

$ time brew install -v ocaml
==> Installing opam dependency: objective-caml
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/objective-caml-4.00.1.lion.bottle.tar.gz
Already downloaded: /Users/tmaeda/Library/Caches/Homebrew/objective-caml-4.00.1.lion.bottle.tar.gz
==> Pouring objective-caml-4.00.1.lion.bottle.tar.gz
/usr/bin/tar xf /Users/tmaeda/Library/Caches/Homebrew/objective-caml-4.00.1.lion.bottle.tar.gz
==> Finishing up
ln -s ../Cellar/objective-caml/4.00.1/bin/ocamlyacc ocamlyacc
(中略)
==> Summary
/usr/local/Cellar/objective-caml/4.00.1: 1187 files, 209M

real    0m12.321s
user    0m6.090s
sys     0m4.586s
$ which ocaml
/usr/local/bin/ocaml
$ ocaml -version
The OCaml toplevel, version 4.00.1

早いですね。バイナリで配布されてるようだ。

opam経由でocamlspot機能付きでocaml入れ直す

brew で opam 入れる

opam ってのはパッケージ管理システムで、OCaml用の 各種ライブラリやソフトウェアをお手軽に取得・ビルドできるってものなんだけど、 なんとOCamlのコンパイラ自身もこいつでインストールすることができる。 Rubyに喩えるなら gem と rvm の機能を opam ひとつで担っている感じ。

brew で入れるので、これもあまり説明の必要がないですね。

$ time brew install -v opam
==> Downloading https://github.com/OCamlPro/opam/tarball/0.7.7
Already downloaded: /Users/tmaeda/Library/Caches/Homebrew/opam-0.7.7.tgz
/usr/bin/tar xf /Users/tmaeda/Library/Caches/Homebrew/opam-0.7.7.tgz
==> ./configure --prefix=/usr/local/Cellar/opam/0.7.7
./configure --prefix=/usr/local/Cellar/opam/0.7.7
(中略)
==> make install
make install
mkdir -p /usr/local/Cellar/opam/0.7.7/bin
/usr/bin/make opam-install opam-mk-repo-install
install _obuild/opam/opam.asm
install _obuild/opam-mk-repo/opam-mk-repo.asm
mkdir -p /usr/local/Cellar/opam/0.7.7/share/man/man1 && cp doc/man/* /usr/local/Cellar/opam/0.7.7/share/man/man1
==> Cleaning
==> Caveats
OPAM uses ~/.opam by default to install packages, so you need to initialize
the package database first by running (as a normal user):

$  opam init

and add the following line to ~/.profile to initialize the environment:

$  eval `opam config -env`

Documentation and tutorials are available at http://opam.ocamlpro.com
==> Finishing up
ln -s ../Cellar/opam/0.7.7/bin/opam-mk-repo opam-mk-repo
ln -s ../Cellar/opam/0.7.7/bin/opam opam
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam.1 opam.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-upload.1 opam-upload.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-upgrade.1 opam-upgrade.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-update.1 opam-update.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-switch.1 opam-switch.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-search.1 opam-search.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-remove.1 opam-remove.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-remote.1 opam-remote.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-reinstall.1 opam-reinstall.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-pin.1 opam-pin.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-list.1 opam-list.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-install.1 opam-install.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-init.1 opam-init.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-info.1 opam-info.1
ln -s ../../../Cellar/opam/0.7.7/share/man/man1/opam-config.1 opam-config.1
ln -s ../../Cellar/opam/0.7.7 opam
ln -s ../Cellar/opam/0.7.7 opam
==> Summary
/usr/local/Cellar/opam/0.7.7: 22 files, 7.3M, built in 40 seconds

real    0m40.329s
user    0m23.127s
sys     0m8.502s

$ which opam
/usr/local/bin/opam
$ opam --version
opam version 0.7.7

Copyright (C) 2012 OCamlPro - INRIA

This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$

うまく行くと上記のように40秒ぐらいで終わる(on MacBook Air 11inch 2012)が、 たまに

tar xfz ocamlgraph-1.8.1.tar.gz
rm -rf ocamlgraph
mv ocamlgraph-1.8.1 ocamlgraph
100  232k  100  232k    0     0  35205      0  0:00:06  0:00:06 --:--:-- 48537

のところで止まって、全然先に進まないことがあった。

おそらく次の

tar xfz dose3-3.1.1.tar.gz
rm -rf dose
mv dose3-3.1.1 dose

っていうやつをダウンロードする際に、ネットワークかサーバーの調子などで うまく行かないことがあるんだと思うので、数分待っても先に進まないようなら、 何度かやり直した方が良いと思う。

あと、opam は現在絶賛開発中で、バージョンが上がると、設定ファイルの フォーマットが変わったり、大きく機能が変更されたりするので、 バージョンアップすると今までの設定ファイルが読み込めなくてうまく 動かなくなったり、blogなどに書いてある方法を試しても その通りに動かなかったりということが現時点ではまだよくあるので、 そういう心構えで使いましょう。

動かなくなっても最悪またゼロからインストールすれば良いよ。

opam の設定

さて、上記のインストールプロセスの最後に出ているように opam init したり、 eval `opam config -env` をshellの設定ファイルに書いたりしないといけない。

$ time opam init
Synchronizing with http://opam.ocamlpro.com ...
The following actions will be performed:
 - install base-threads.base
 - install base-bigarray.base
 - install base-unix.base
3 to install | 0 to reinstall | 0 to upgrade | 0 to downgrade | 0 to remove

=-=-= base-bigarray.base =-=-=
Downloading http://opam.ocamlpro.com/archives/base-bigarray.base+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/base-bigarray.base+opam.tar.gz
Build commands:
  ./build.sh
Installing base-bigarray.base

=-=-= base-threads.base =-=-=
Downloading http://opam.ocamlpro.com/archives/base-threads.base+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/base-threads.base+opam.tar.gz
Build commands:
  ./build.sh
Installing base-threads.base

=-=-= base-unix.base =-=-=
Downloading http://opam.ocamlpro.com/archives/base-unix.base+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/base-unix.base+opam.tar.gz
Build commands:
  ./build.sh
Installing base-unix.base

real    0m11.399s
user    0m1.681s
sys     0m1.862s

$

これでホームディレクトリの下に ~/.opam ってディレクトリができて、 ここに各種必要なファイルなどが生成される。

opam では $ opam switch でコンパイラーを切り替えられるんだけど、 切り替える度に手で eval `opam config -env` しろって言われて、 それを自動化するための スクリプト もあるんだけど、たぶんこの記事を必要とするOCaml入門な人は そんなに頻繁にコンパイラ切り替えたりしないと思うので、スクリプト使うなり 手でやるなり好きにしてください。

opam で annot パッチ付きの ocaml を入れる

これから opam を使っていろいろパッケージを入れたくなるんだけど、 その前にちょっとひと手間。

ocamlcやocamloptに、 -annot とか -bin-annot というオプションをつけてコンパイルすると コンパイル結果のバイナリの他に.annotや.cmtという拡張子がついたファイルが 生成されるようになる。このファイルにはソースコードの解析結果(変数や関数の型とか)が 書かれているので、これと ocamlspot というソフトを利用して、 型や関数の定義場所に一瞬で飛んで行けるようになり、非常にソースコードを読むのが 楽になる。

しかし、opam 経由でコンパイルされる際には -annot や -bin-annot 付きで コンパイルされるとは限らないので、 opam 経由でインストールするライブラリは そのままでは ocamlspot でサクサク読めない。

で、ocamlspot 作者のcamlspotterさんが、 環境変数 OCAML_ANNOT=1 を指定しておけば、常に-annot -bin-annot付きで コンパイルされるようになるパッチを公開してくれているので、 このパッチを当てたコンパイラを opam で入れ直す。

$ opam switch -list
--- Installed compilers ---
 * system

--- Available compilers ---
   3.12.1
   3.12.1   (3.12.1+mirage-unix-direct)
   3.12.1   (3.12.1+mirage-xen)
   4.00.0
   4.00.0   (4.00.0+debug-runtime)
   4.00.0   (4.00.0+fp)
   4.00.0   (4.00.0+jocaml)
   4.00.0   (4.00.0+raspberrypi)
   4.00.1
   4.00.1   (4.00.1+annot)
   4.00.1   (4.00.1+raspberrypi)
   4.00.1   (4.00.1+short-types)
 ~ 4.00.1   (system)

4.00.1+annot ってのが見えますね。ではこれをインストールしましょう。 インストールする前に、環境変数 OCAML_ANNOT=1 を設定しておくと、 ocaml の標準ライブラリがビルドされる際にも -annot 付きで コンパイルされるので、嬉しい事がある、、、はず。

$ export OCAML_ANNOT=1
$ time opam switch 4.00.1+annot
(中略)
To update $PATH, $MANPATH, $OCAML_TOPLEVEL_PATH, $CAML_LD_LIBRARY_PATH; you can now run:

    $ eval `opam config -env`


real    38m14.371s
user    36m7.670s
sys     0m59.686s

だいぶ時間かかりますねー。

ocamlspot 関係のパッケージを入れる

これ以降、opam でいろいろなパッケージを入れていきますが、 引き続き $ export OCAML_ANNOT=1 な環境で実行していき、 .annot や .cmt が生成される状態でインストールしていきます。

まずは .annot とか .cmt を適切な場所に入れてくれる spotinstall というツール。

$ time opam install spotinstall
The following actions will be performed:
 - install ocamlfind.1.3.3
 - install omake.0.9.8.6-0.rc1
 - install spotlib.2.0.1
 - install spotinstall.1.0.0
4 to install | 0 to reinstall | 0 to upgrade | 0 to downgrade | 0 to remove
Do you want to continue ? [Y/n] y

=-=-= ocamlfind.1.3.3 =-=-=
The archive for ocamlfind.1.3.3 is in the local cache.
Extracting /Users/tmaeda/.opam/archives/ocamlfind.1.3.3+opam.tar.gz
Build commands:
  ./configure -bindir /Users/tmaeda/.opam/4.00.1+annot/bin -sitelib /Users/tmaeda/.opam/4.00.1+annot/lib -mandir /Users/tmaeda/.opam/4.00.1+annot/man -config /Users/tmaeda/.opam/4.00.1+annot/lib/findlib.conf
  make all
  make opt
  make install
Installing ocamlfind.1.3.3

=-=-= omake.0.9.8.6-0.rc1 =-=-=
The archive for omake.0.9.8.6-0.rc1 is in the local cache.
Extracting /Users/tmaeda/.opam/archives/omake.0.9.8.6-0.rc1+opam.tar.gz
Applying opam.patch
Build commands:
  make bootstrap PREFIX=/Users/tmaeda/.opam/4.00.1+annot
  make all PREFIX=/Users/tmaeda/.opam/4.00.1+annot
  make install PREFIX=/Users/tmaeda/.opam/4.00.1+annot
Installing omake.0.9.8.6-0.rc1

=-=-= spotlib.2.0.1 =-=-=
The archive for spotlib.2.0.1 is in the local cache.
Extracting /Users/tmaeda/.opam/archives/spotlib.2.0.1+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing spotlib.2.0.1

=-=-= spotinstall.1.0.0 =-=-=
The archive for spotinstall.1.0.0 is in the local cache.
Extracting /Users/tmaeda/.opam/archives/spotinstall.1.0.0+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing spotinstall.1.0.0

real    8m2.623s
user    9m54.915s
sys     0m40.680s

$ which spotinstall
/Users/tmaeda/.opam/4.00.1+annot/bin/spotinstall

次に、.annot や .cmt を利用して変数や関数の定義場所を 探してくれる ocamlspot というツール。

$ time opam install ocamlspot
The following actions will be performed:
 - install ocamlspot.4.00.0.2.0.1
1 to install | 0 to reinstall | 0 to upgrade | 0 to downgrade | 0 to remove

=-=-= ocamlspot.4.00.0.2.0.1 =-=-=
The archive for ocamlspot.4.00.0.2.0.1 is in the local cache.
Extracting /Users/tmaeda/.opam/archives/ocamlspot.4.00.0.2.0.1+opam.tar.gz
Build commands:
  make all opt install BINDIR=/Users/tmaeda/.opam/4.00.1+annot/bin EMACSDIR=/Users/tmaeda/.opam/4.00.1+annot/lib/ocamlspot PREFIX=/Users/tmaeda/.opam/4.00.1+annot
  cp ocamlspot.vim /Users/tmaeda/.opam/4.00.1+annot/lib/ocamlspot

Installing ocamlspot.4.00.0.2.0.1

real    1m3.277s
user    0m59.064s
sys     0m3.035s

$ which ocamlspot
/Users/tmaeda/.opam/4.00.1+annot/bin/ocamlspot

その他、便利なパッケージを入れる

utop

次に utop。より強力なtop levelといったところでしょうか。 rubyで言う所の pry。

$ time opam install utop
The following actions will be performed:
 - install camomile.0.8.3
 - install react.0.9.4
 - install lwt.2.4.2
 - install zed.1.2
 - install lambda-term.1.2
 - install utop.1.2.1
6 to install | 0 to reinstall | 0 to upgrade | 0 to downgrade | 0 to remove
Do you want to continue ? [Y/n] y

=-=-= camomile.0.8.3 =-=-=
Downloading http://opam.ocamlpro.com/archives/camomile.0.8.3+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/camomile.0.8.3+opam.tar.gz
Build commands:
  ./configure --prefix /Users/tmaeda/.opam/4.00.1+annot --sbindir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/sbin --libexecdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/libexec --sysconfdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/etc --sharedstatedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/com --localstatedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/var --libdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/lib --includedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/include --datarootdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/share
  make
  make install
Installing camomile.0.8.3

=-=-= react.0.9.4 =-=-=
Downloading http://opam.ocamlpro.com/archives/react.0.9.4+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/react.0.9.4+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing react.0.9.4

=-=-= lwt.2.4.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/lwt.2.4.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/lwt.2.4.2+opam.tar.gz
Build commands:
  ./configure --disable-libev --enable-react --disable-ssl --enable-unix --enable-extra --enable-preemptive
  make build
  make install
Installing lwt.2.4.2

=-=-= zed.1.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/zed.1.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/zed.1.2+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing zed.1.2

=-=-= lambda-term.1.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/lambda-term.1.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/lambda-term.1.2+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing lambda-term.1.2

=-=-= utop.1.2.1 =-=-=
Downloading http://opam.ocamlpro.com/archives/utop.1.2.1+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/utop.1.2.1+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing utop.1.2.1

real    6m21.436s
user    3m53.935s
sys     0m25.729s

$

後はコマンドラインから以下のように起動する。

$ utop

すると、こんな感じになる。派手!

utop

しかもインクリメンタルサーチでモジュール名や関数名の 補完ができる。一応、emacs用のインターフェースもあるらしいけど、 うまく動かせたことがない。

batteries

次に batteries。rubyで言うところの ActiveSupport。 便利なモジュールや関数がいっぱい定義されてます。

$ time opam install utop
The following actions will be performed:
 - install camomile.0.8.3
 - install react.0.9.4
 - install lwt.2.4.2
 - install zed.1.2
 - install lambda-term.1.2
 - install utop.1.2.1
6 to install | 0 to reinstall | 0 to upgrade | 0 to downgrade | 0 to remove
Do you want to continue ? [Y/n] y

=-=-= camomile.0.8.3 =-=-=
Downloading http://opam.ocamlpro.com/archives/camomile.0.8.3+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/camomile.0.8.3+opam.tar.gz
Build commands:
  ./configure --prefix /Users/tmaeda/.opam/4.00.1+annot --sbindir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/sbin --libexecdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/libexec --sysconfdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/etc --sharedstatedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/com --localstatedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/var --libdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/lib --includedir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/include --datarootdir=/Users/tmaeda/.opam/4.00.1+annot/lib/camomile/share
  make
  make install
Installing camomile.0.8.3

=-=-= react.0.9.4 =-=-=
Downloading http://opam.ocamlpro.com/archives/react.0.9.4+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/react.0.9.4+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing react.0.9.4

=-=-= lwt.2.4.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/lwt.2.4.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/lwt.2.4.2+opam.tar.gz
Build commands:
  ./configure --disable-libev --enable-react --disable-ssl --enable-unix --enable-extra --enable-preemptive
  make build
  make install
Installing lwt.2.4.2

=-=-= zed.1.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/zed.1.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/zed.1.2+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing zed.1.2

=-=-= lambda-term.1.2 =-=-=
Downloading http://opam.ocamlpro.com/archives/lambda-term.1.2+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/lambda-term.1.2+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing lambda-term.1.2

=-=-= utop.1.2.1 =-=-=
Downloading http://opam.ocamlpro.com/archives/utop.1.2.1+opam.tar.gz ...
Extracting /Users/tmaeda/.opam/archives/utop.1.2.1+opam.tar.gz
Build commands:
  ocaml setup.ml -configure --prefix /Users/tmaeda/.opam/4.00.1+annot
  ocaml setup.ml -build
  ocaml setup.ml -install
Installing utop.1.2.1

real    6m21.436s
user    3m53.935s
sys     0m25.729s

$

で、~/.ocamlinit に以下を記述(~/.opam/4.00.1+annot/lib/batteries/ocamlinitの中身をコピペ)。

let interactive = !Sys.interactive;;
Sys.interactive := false;; (*Pretend to be in non-interactive mode*)
#use "topfind";;
Sys.interactive := interactive;; (*Return to regular interactive mode*)

Toploop.use_silently 
             Format.err_formatter (Filename.concat (Findlib.package_directory 
             "batteries") "battop.ml");;

で、ocaml を起動すると、以下のように電池(batteries)のアスキーアート付きの toplevel(対話シェル)に変わる。

batteries toplevel

.ocamlinitに書いた #use "topfind" によって、topfind が 利用可能になる。 topfind ってのは、ocaml のライブラリを探す ocamlfind というソフトウェアの機能の一つで、 toplevelからも ocamlfind の機能を利用できるようにするものらしい。 Ruby で言う所の require 'rubygems' みたいなものか?

で、その後の Toploop ほにゃららってので、ocaml 標準の toplevel 内で、 引数で指定した battop.ml ってのを実行する。 battop.ml ってのは batteries が定義している toplevel の機能で、この中で #require "batteries" などが行われるために、batteries の各種モジュールが 利用可能になったり、電池のAAが出たりするらしい。

この状態で utop を起動すると、さっきよりも更にいろいろ出る。

batteries utop

batteries によっていろいろなモジュールや関数が定義されるので、 以下のような範囲を指定したloopなども簡単にかける。

$ (1--100) |> Enum.reduce (+);;
- : int = 5050 

ちなみに、型はこんな感じ。

$ (--);;
- : int -> int -> int BatEnum.t = <fun>

$ (|>);;
- : 'a -> ('a -> 'b) -> 'b = <fun>

他にもモナドとか無限リストとかリスト内包表記とかいろいろある。

Getting Started とか New apis とか api document 参照。

omakeを入れる

名前からおわかりのように、OCaml で実装された make。 依存関係の解決が非常に強力らしい。

あと、omake -P すると omake がずっと起動しっぱなしになって、 ファイルの変更を検知すると、自動的に再びビルドが走るという機能が 付いている。 ruby でいうところの、 guard とか watchr みたいな機能が rake に内蔵されてる感じ。この機能を使えば、自分でビルドしなくても 勝手にビルドが走るので、自分の書いたコードの型チェックを自動で 走らせながら、非常に高速に開発できる。

で、インストールですが、上記の spotinstall を入れたときに、 自動的に入ったので、自分で入れる必要は無し。

Emacsにtuaregとocamlspotを入れる

tuareg は以下から取得。

<URL:https://forge.ocamlcore.org/frs/?group_id=43>

$ tar xfvz tuareg-2.0.6.tar.gz
$ cd tuareg-2.0.6

中身を眺めるとMakefileがあるので、make を叩いてみるが、

Makefile:97: *** missing separator.  Stop.

とエラーする。なんだろう?makeのバージョンの違いとか? よくわからないけど、不要な箇所なので、

$(DIST_NAME).tar.gz $(DIST_NAME).tar: $(DIST_FILES)
	mkdir -p $(DIST_NAME)
	for f in $(DIST_FILES); do $(LN) $$f $(DIST_NAME); done
	echo '(define-package "tuareg" "$(VERSION)" "$(DESCRIPTION)" ' "'"'$(REQUIREMENTS))' > $(DIST_NAME)/tuareg-pkg.el
	tar acvf $@ $(DIST_NAME)
	$(RM) -rf $(DIST_NAME)

をまるっと削って、make する。

$ make EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
/Applications/Emacs.app/Contents/MacOS/Emacs -batch  -f batch-byte-compile tuareg.el
Wrote /Users/tmaeda/rpm/SOURCES/tuareg-2.0.6/tuareg.elc
/Applications/Emacs.app/Contents/MacOS/Emacs -batch  -f batch-byte-compile ocamldebug.el
Wrote /Users/tmaeda/rpm/SOURCES/tuareg-2.0.6/ocamldebug.elc
echo "\
        ;;; tuareg-site-file.el --- Automatically extracted autoloads.\n\
        ;;; Code:\n\
        (add-to-list 'load-path\n\
                     (or (file-name-directory load-file-name) (car load-path)))\n\
        " >tuareg-site-file.el
/Applications/Emacs.app/Contents/MacOS/Emacs --batch --eval '(setq generated-autoload-file "'`pwd`'/tuareg-site-file.el")' -f batch-update-autoloads "."
Generating autoloads for ocamldebug.el...
Generating autoloads for ocamldebug.el...done
Generating autoloads for tuareg-pkg.el...
Generating autoloads for tuareg-pkg.el...done
Generating autoloads for tuareg.el...
Generating autoloads for tuareg.el...done
Saving file /Users/tmaeda/rpm/SOURCES/tuareg-2.0.6/tuareg-site-file.el...
Wrote /Users/tmaeda/rpm/SOURCES/tuareg-2.0.6/tuareg-site-file.el
(No changes need to be saved)

install ターゲットが定義されてないみたいなので、手でコピーする。 コピー先は自分がelispのインストール場所として使っている場所 (load-pathが通ってる場所)に読み替えてください。

$ cp -p *.el{,c} ~/.emacs.d/site-lisp/

次に ocamlspot.el。これは opam install ocamlspot したときに、 /Users/tmaeda/.opam/4.00.1+annot/build/ocamlspot.4.00.0.2.0.1/ocamlspot.el などに入っているので、後は M-x auto-install-from-buffer などで入れるだけ。

で、以下のような設定を ~/.emacs.d/init.el などに書きましょう。 私はinit-loaderを入れているので、 ~/.emacs.d/inits/50-ocaml.el に書いてますけども。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; tuareg-mode
(setq auto-mode-alist (cons '("\\.ml[iylp]?$" . tuareg-mode) auto-mode-alist))
(autoload 'tuareg-mode "tuareg" "Major mode for editing Caml code" t)
(require 'tuareg)
(autoload 'camldebug "camldebug" "Run the Caml debugger" t)

(setenv "PATH"
        (concat '"/Users/tmaeda/.opam/4.00.1+annot/bin:" (getenv "PATH")))
;(setq ocamlspot-command "/usr/local/bin/ocamlspot")
(setq ocamlspot-command "/Users/tmaeda/.opam/4.00.1+annot/bin/ocamlspot")
(setq tuareg-interactive-program "/Users/tmaeda/.opam/4.00.1+annot/bin/ocaml")
(setq omake-program-path "/Users/tmaeda/.opam/4.00.1+annot/bin/omake")
;(setq omake-program-arguments "-P -w --verbose")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; flymake
;; cf) http://d.hatena.ne.jp/osiire/20120613
(require 'flymake)
(push '("File \"\\(.*\\)\", line \\([0-9]+\\), characters \\([0-9]+\\)--?\\([0-9]+\\):\\(.*\\)" 1 2 3 5) flymake-err-line-patterns)
(push '("\\.ml\\'" flymake-ocaml-init) flymake-allowed-file-name-masks)
(defun flymake-ocaml-init ()
  (list "/bin/bash" (list "-c" (format "%s -s 2>&1 | tr -d \"\\n\" | sed -Ee 's/[[:space:]]+/ /g'" omake-program-path))))
(custom-set-faces
 '(flymake-errline ((((class color)) (:background "LightYellow" :underline "OrangeRed"))))
 '(flymake-warnline ((((class color)) (:background "LightBlue2" :underline "Yellow")))))

;; cf) http://d.hatena.ne.jp/khiker/20070720/emacs_flymake
(defun credmp/flymake-display-err-minibuf () 
  "Displays the error/warning for the current line in the minibuffer"
  (interactive)
  (let* ((line-no             (flymake-current-line-no))
         (line-err-info-list  (nth 0 (flymake-find-err-info flymake-err-info line-no)))
         (count               (length line-err-info-list))
         )
    (while (> count 0)
      (when line-err-info-list
        (let* ((file       (flymake-ler-file (nth (1- count) line-err-info-list)))
               (full-file  (flymake-ler-full-file (nth (1- count) line-err-info-list)))
               (text (flymake-ler-text (nth (1- count) line-err-info-list)))
               (line       (flymake-ler-line (nth (1- count) line-err-info-list))))
          (message "[%s] %s" line text)
          )
        )
      (setq count (1- count)))))
(defadvice flymake-goto-prev-error (after flymake-goto-prev-error-display-message activate)
  (credmp/flymake-display-err-minibuf))
(defadvice flymake-goto-next-error (after flymake-goto-next-error-display-message activate)
  (credmp/flymake-display-err-minibuf))


(require 'ocamlspot)

(defun run-ocamlspot-type ()
  (if (eq major-mode 'tuareg-mode)
      (ocamlspot-type)
      )
  )

(defvar ocamlspot-type-timer nil) 
(defun toggle-ocamlspot-type-timer ()
  (interactive)
  (if ocamlspot-type-timer
      (progn
        (anything-new-timer 'ocamlspot-type-timer nil)
        (message "auto ocamlspot-type disabled")
        )
      (anything-new-timer 'ocamlspot-type-timer
                          (run-with-idle-timer 0.5 t 'run-ocamlspot-type)
                          )
      (message "auto ocamlspot-type enabled")

    )

  )

(defun tuareg-mode-init ()
  ;; indentation rules
  (setq tuareg-lazy-= t)
  (setq tuareg-lazy-paren t)
  (setq tuareg-in-indent 0)
  (setq tuareg-electric-indent nil)
  (setq tuareg-leading-star-in-doc t)
  (setq tuareg-with-indent 0)

  ;    (setq tuareg-library-path "/usr/local/lib/ocaml/")
  (setq tuareg-library-path "/Users/tmaeda/.opam/4.00.1+annot/lib")

  ;; turn on auto-fill minor mode
  (auto-fill-mode 1)

  ;; Sym-Lock customization only
  ;; turn off special face under mouse
  (if (featurep 'sym-lock)   
      (setq sym-lock-mouse-face-enabled nil))

  ;; ocamlspot and other keys
  (local-set-key "\C-c;" 'ocamlspot-query)
  (local-set-key "\C-c:" 'ocamlspot-query-interface)
  (local-set-key "\C-c'" 'ocamlspot-query-uses)
;    (local-set-key "\C-c\C-t" 'ocamlspot-type)
  (local-set-key "\C-c\C-i" 'ocamlspot-xtype)
  (local-set-key "\C-c\C-y" 'ocamlspot-type-and-copy)
  (local-set-key "\C-cx" 'ocamlspot-expand)
  (local-set-key "\C-c\C-u" 'ocamlspot-use)
  (local-set-key "\C-ct" 'caml-types-show-type)
  (local-set-key "\C-cp" 'ocamlspot-pop-jump-stack)
  (local-set-key "\C-c\C-t" 'toggle-ocamlspot-type-timer)

  (flymake-mode-on)
  )

(add-hook 'tuareg-mode-hook 'tuareg-mode-init)
(setq which-func-modes (append which-func-modes '(tuareg-mode)))

;; You can also change overlay colors as follows:
; (set-face-background 'ocamlspot-spot-face "#660000")
; (set-face-background 'ocamlspot-tree-face "#006600")

で、上記を M-x eval-region するなり、Emacsを再起動するなりしましょう。

omake を試してみる

では簡単なソースコードでomakeを試してみましょう。 まずはソースコードを入れるディレクトリを作成します。

$ mkdir omaketest

次に、そのディレクトリの中に omake の設定ファイルを生成します。

$ cd omaketest
$ omake --install

これで、OMakefile と OMakeroot というファイルが出来上がります。

OMakeroot というのは、プロジェクトのトップディレクトリを 表すものなので、トップディレクトリにしか置かないファイルです。

OMakefile がビルドのルールを定義するものです。 では、エディタで OMakefile を開いて、以下を記述しましょう。

.PHONY: all run clean

USE_OCAMLFIND = true
FILES[] =
     foo # ビルド対象のソースコードの名前(拡張子不要)

PROGRAM = foo # ビルドした結果できるバイナリのファイル名
OCAMLPACKS[] += batteries # リンクするライブラリ
#OCAMLFLAGS += -thread
OCAMLFLAGS += -annot -bin-annot # コンパイラに渡すフラグで ocamlspot を使えるように
.DEFAULT: run # デフォルトのターゲットは run だよ
all: $(OCamlProgram $(PROGRAM), $(FILES))

clean:
	rm -f *~ $(PROGRAM) *.opt *.cmi *.cmx *.o *.omc *.cmt *.annot

run: all # run は all に依存するよ
  ./$(PROGRAM)

次に、簡単なソースコードを書きましょう。 foo.ml という名前で batteries のサンプルコードを打ち込んでみましょう。

open Batteries_uni

let main () =
  (1--999) (* the enum that counts from 1 to 999 *)
  |> Enum.filter (fun i -> i mod 3 = 0 || i mod 5 = 0)
  |> Enum.reduce (+) (* add all remaining values together *)
  |> Int.print stdout

let () = main ()

これで omake を実行してビルドしてみましょう。

$ omake
*** omake: reading OMakefiles
*** omake: finished reading OMakefiles (0.01 sec)
- build . <run>
+ ./foo
233168[==================================================================================================================             ] 00019 / 0*** omake: done (0.67 sec, 0/1 scans, 4/6 rules, 5/90 digests)

というように、ビルドされて、実際に実行まで行われて 結果の 233168 というのが表示されています。

また、 omake -P を実行すると、ファイルの更新を検知して ずっとビルドを繰り返してくれますので、いちいち自分で

  • ソースコード直す
  • omake でビルドする
  • ビルドに失敗したらソースコード直す
  • ビルドに成功したら実行して結果を確認する

という作業をしなくて済みます。素晴らしい!

例えば、上記のソースコードの (1--999) を (1--99) に書き換えて保存すると

$ omake -P
*** omake: reading OMakefiles
*** omake: finished reading OMakefiles (0.01 sec)
- build . <run>
+ ./foo
233168[==================================================================================================================             ] 00019 / 0*** omake: done (0.67 sec, 0/1 scans, 4/6 rules, 5/99 digests)
*** omake: polling for filesystem changes
*** omake: file foo.ml changed
*** omake: the project is currently locked.
*** omake: the project was last locked by macair.localhost.localdomain:39969.
*** omake: waiting for project lock: .*** omake: another OMake process have modified the build DB, restarting
*** omake: reading OMakefiles
*** omake: finished reading OMakefiles (0.01 sec)
- build . <run>
+ ./foo
2318[==================================================================================================================             ] 00019 / 000*** omake: done (33.19 sec, 1/1 scans, 4/6 rules, 5/99 digests)
*** omake: polling for filesystem changes

ってな感じで、ファイルシステムをポーリングして、 更新を検知し、自動的にビルドしなおして、実行結果の 2318 が出力 されていることがわかりますね。

lock がうんちゃらというエラーみたいのが 出てるけど、もしかして flymake の omake とぶつかってるんだろうか...

tuareg を試してみる

シンタックスカラーリング、Emacs内でREPLの起動、今自分が書いている ソースコードの一部をREPLに送って評価したりできる。

REPL(toplevel) の起動

既に何かの .ml ファイルを開いていて、 tuareg-mode になっている のであれば、C-c C-s すれば、toplevel のコマンドを指定しろと 言われるので、デフォルトのまま(/Users/tmaeda/.opam/4.00.1+annot/bin/ocaml) で Enter。

tuareg toplevel

すると、こんな風に Emacs の中に toplevel が起動する。

tuareg toplevel

今書いているコードの一部を REPL で評価

例えば、今書いているコードの Int.print の型ってなんだっけ? ってわからなくなったときは、 Int.print をリージョンで選択した 状態で、C-c C-r すると、その部分が toplevel に送られて評価され、 型が 'a BatInnerIO.output -> int -> unit = <fun> だとわかる。

tuareg toplevel

その他

他にも定型句を入力する機能とかあるけど、詳しくは メニュー見るなり、C-h m や C-h b で調べるなり、 ソース読むなりしましょう。

tuareg menu

といっても、定型句は yasnippet とか auto-complete とかでやるよね。

flymake を試してみる

omake -P があれば要らないような気もするが...エラーにすぐに 飛んで行けるのは便利。

例えば、上記のコードの

|> Int.print stdout

の行を削除してみましょう。 すると、 flymake で omake が走るようにしてあるので、 omake でビルドされて、エラーの行が赤くなるので、M-N などで すぐにエラーに飛んで行けると同時に、ミニバッファにエラーの 内容が表示されます。

omake flymake

omake-mode が動くのが一番幸せな気がするが、私の環境ではなぜか Emacs に omake の結果が表示されたりされなかったりで、 うまく動かなかったので、断念中。いずれ時間かけて調べてみたい。

ocamlspot を試してみる

では、ocamlspotを試してみましょう。ocamlspotを利用するには、 コンパイルで生成されたcmi/cma/cmxaと一緒にcmt/cmtiなどが 存在していないといけないらしい。

しかし当然普通のMakefileなりビルドスクリプトなりは ocamlspot のことを考慮してはいないので、cmt/cmtiなどは インストールされない。 そのインストールをほぼ自動化してくれるのが同じく camlspotterさんが作ってくれているspotinstallというツール。

以下のように実行すると、とりあえずさっき入れたocamlの 標準ライブラリ用の cmt/cmti などが適切な場所にコピーされる。

$ spotinstall -v ocaml
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arg.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arg.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arg.p.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arith_flags.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arith_status.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arith_status.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/array.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/array.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/array.p.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arrayLabels.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arrayLabels.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/arrayLabels.p.cmx
(中略)
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters/Camlp4TrashRemover.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters/Camlp4TrashRemover.cmo
found /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters/Camlp4TrashRemover.cmx
Copied ./.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top/Rprint.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top
Copied ./.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top/Rprint.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top
Copied ./.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top/Rprint.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top
Copied ./.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top/Top.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Top
(中略)
Copied ./.opam/4.00.1+annot/build/ocaml/_build/camlp4/Camlp4Filters/Camlp4TrashRemover.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters
Copied ./.opam/4.00.1+annot/build/ocaml/_build/camlp4/Camlp4Filters/Camlp4TrashRemover.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters
Copied ./.opam/4.00.1+annot/build/ocaml/_build/camlp4/Camlp4Filters/Camlp4TrashRemover.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/camlp4/Camlp4Filters
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/outcometree.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/cmx_format.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/parsetree.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/topdirs.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/topdirs.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/topmain.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/expunge.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/topstart.cmo
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/asttypes.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/toploop.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/genprintval.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/cmo_format.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/topstart.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/annot.cmi
No spot/spit/cmt/cmti found for /Users/tmaeda/.opam/4.00.1+annot/lib/ocaml/compiler-libs/trace.cmi

あと、他にもいくつかライブラリっぽいものを入れたので、それらも同じくspotinstallする。 なんか、ocaml のトップディレクトリに居る状態じゃないと、うまくファイルを見つけられない???ので、一応 cd してから実行。

$ cd ~/.opam/4.00.1+annot/
$ spotinstall -v spotlib
found /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib/spotlib.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib/spotlib.cmo
found /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib/spotlib.cmx
Copied ./build/spotlib.2.0.1/lib/spotlib.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib
Copied ./build/spotlib.2.0.1/lib/spotlib.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib
Copied ./build/spotlib.2.0.1/lib/spotlib.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/spotlib

同様に、 batteries にも。

$ spotinstall -v batteries
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/batArg.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/batArg.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/batArray.cmi
(中略)
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/batVect.cmi
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/batVect.cmx
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/pa_comprehension.cmo
found /Users/tmaeda/.opam/4.00.1+annot/lib/batteries/pa_llist.cmo
Copied ./build/batteries.1.5.0/_build/src/batArg.cmti to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries
Copied ./build/batteries.1.5.0/_build/src/batArg.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries
Copied ./build/batteries.1.5.0/_build/src/batArray.cmti to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries
(中略)
Copied ./build/batteries.1.5.0/_build/src/batVect.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries
Copied ./build/batteries.1.5.0/_build/src/syntax/pa_llist/pa_llist.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries
Copied ./build/batteries.1.5.0/_build/src/syntax/pa_comprehension/pa_comprehension.cmt to /Users/tmaeda/.opam/4.00.1+annot/lib/batteries

この状態で、例えば ~/.opam/4.00.1+annot/build/spotinstall.1.0.0/spotinstall.ml を開くと こんな感じになる。

ocamlspot

さて、OCaml のソースを読み解く上で非常に難しいのが、

  • いろいろな演算子をユーザーが定義できるので、記号が出てくるとそれが OCaml の文法なのか、関数なのか初学者にはわからない。(しかも記号なのでググってもヒットしない)
  • 記号じゃなかったとしても、モジュールの機能が強力なので、今自分が見ている関数がいったいのどのモジュールのものなのか一目ではわからない

という点です。

上記のソースでも例えば

imp_ None & fun ocamlfind_path ->
  ignore & shell_command "ocamlfind printconf destdir" & function
    | (`Out, `Read line) ->
        begin match !ocamlfind_path with
        | Some _ -> failwith "ocamlfind printconf destdir prints more than one line"

というコードがあり、関数もいろいろわからないことだらけですが、 それに加えて & とか ! とかも出現し、これが一体どういう意味なのか初学者には 非常に難しいです。他の言語から来た人にとって、 & はビット演算ですし、!は否定(not)の意味に見えてしまいます。

では、ocamlspot の威力を見てみましょう。上記の & の上にカーソルを持って行って、

ocamlspot

Emacs で C-c : すると...

ocamlspot

ってな感じで、インターフェース定義のところに飛んで行けて、あー、 Haskell の $ と同じなのね、とかわかる。

同様に、! の上にカーソルを持って行って、

ocamlspot

Emacs で C-c : すると...

ocamlspot

というように、同じくインターフェース定義のところに飛んで行けます。

OCamlは.mliというインターフェースファイルと、.ml という実装ファイルが 分かれており、関数の説明のコメントなどは 大抵 .mli の方に書かれるので、 まずは C-c : でインターフェースを見て、それでもよくわからなければ、 C-c ; で実装を見る、って感じでソースを読んで行くと良いです。

移動先で C-c p すると、元居た場所に戻れます。

他にも C-c C-t すると、カーソルの合わせてある箇所の型が自動的に ミニバッファに表示されるように先程 init.el に書いたので、

ocamlspot-type

というように shell_command 関数の型がミニバッファに表示される。 もう一度、C-c C-t すると、自動型表示モードを off にします。 これらはソースコードを読むときの重要な助けになります。

ocamlbrowserを試してみる

これは ocaml 入れると一緒に入ってくる。

SmalltalkのBrowserみたいなやつ。メソッド名で検索できたり、型で検索できたりする。

ocamlbrowser

一応、EditorとかREPLもあるんだけど、 tuareg があったら不要ですね...

以下の本の最後にも解説がある。

長々とお読み頂きありがとうございました。


2003|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|10|11|12|
2006|01|02|04|05|06|07|08|09|10|11|12|
2007|01|03|06|07|08|09|10|11|12|
2008|01|02|03|04|06|07|10|12|
2009|01|03|06|07|08|09|11|12|
2010|01|02|03|05|06|07|08|09|11|12|
2011|01|03|06|08|09|12|
2012|01|10|
2013|01|03|04|06|
hikiもあるよ。
全文検索:

% grep '' *.td2

2013年
6月
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
本書きました!
Tags:
最近のツッコミ:
SapporoRubyKaigi03 Speaker SapporoRubyKaigi02 Speaker RubyKaigi2009Staff RubyKaigi2008Staff
オススメ!
劇場版マクロスF ~サヨナラノツバサ~ Blu-ray Disk Hybrid Pack (通常版)
劇場版マクロスF~イツワリノウタヒメ~ Blu-ray Disc
劇場版マクロスF~イツワリノウタヒメ~
攻殻機動隊 STAND ALONE COMPLEX DVD-BOX
攻殻機動隊 S.A.C. 2nd GIG DVD-BOX
EMOTION the Best 攻殻機動隊 STAND ALONE COMPLEX Solid State Society
マクロスF ギャラクシーツアーFINAL in ブドーカン
ユニバーサル・バニー
劇場版「空の境界」 俯瞰風景
劇場版「空の境界」 痛覚残留
劇場版 「空の境界」 矛盾螺旋
劇場版「空の境界」殺人考察(後)