はじめに
こんにちは、Python界の情弱です。ちょっと前にOCaml系のエントリを色々と眺めていたらYaron Minsky氏のエントリを見つけたので翻訳してみました。
Yaron Minsky氏はJane Streetで第一線で活躍されるエンジニアで、Jane Streetの技術ページをはじめ多くの場所でOCamlに関しての知見を語ってくださっています。
本エントリはJohn Hughesの名エントリ「なぜ関数プログラミングは重要か」を受けてACM Queueに寄稿されたものの日本語訳です。
Why the next language you learn should be functional
YARON MINSKY, JANE STREET
エレガントな実装は関数である。メソッドでも、クラスでも、フレームワークでもなく、ただの関数である。 - John Carmack(訳注 : John Carmack氏はアメリカのゲームプログラマで、FPSの生みの親と言われています。*1)
関数型プログラミングは卓越した歴史を持つ、昔からある発想です。Alonzo Churchのラムダ計算に刺激されたLispは計算機の歴史の夜明けから発展してきた最初の関数型言語の一つです。OCamlやHaskellのような関数型の静的型付け言語は新しいですが、そのルーツはかなり昔にさかのぼります。--その先祖であるMLは、70年代初頭にRobin Milnerが行った先駆的なLCF(Logic for Computable Functions)定理証明器に関する研究までさかのぼります。
また関数型プログラミングは多大なる影響を与え続けて来ました。言語設計における多くの基礎的なレベルでの先駆性、ガベージコレクションから型推論へのジェネリクスに至るまで、が関数型の世界から飛び出して、他の言語で実装されるよりも何十年も前に関数型の世界では当たり前のものになっていました。
それでもなお関数型言語は本当の意味でまだ主流にはなったことはありません。SymbolicsやLispマシンが流行った時代にはおそらくかなり良い線までは行っていましたが、それもいまや昔の話です。ここ数年の関数型プログラミングの復権にも関わらず、その技術自体は実際に使われると言うよりも議論の対象にとどまっています。
こうやってみると関数型言語にはなにか必要な物が足りないと結論づけたくなります。関数型言語は特定のアプリケーションでは非常に理にかなっていて、他の言語にも取り込まれるような有用な概念も持っています。しかし、命令形言語やオブジェクト指向言語は単純にソフトウェアエンジニアリングにおける圧倒的多数のタスクに向いていると言いたくなります。
魅力的に見えますが、この結論は間違っています。私は10年近くプロダクション環境においてOCamlを使ってきました。そして時が経つにつれ、私は関数型言語、特にOCamlやHaskellのような強い型付けの言語は完璧な汎用目的のプログラミングツールであると確信しました。--現存する他のどんな主流言語と比較してもです。これらの関数型言語は幅広くもあります。小さいスクリプトタスクから大規模高パフォーマンスアプリケーションに至るまで適しています。すべての仕事に向いているとは言いませんが、かなり近いところまでいっています。
OCamlへの移行
私のOCamlによるプログラミングの経験のほとんどはJane Streetでの仕事上におけるものです。Jane Streetは2000年に設立された金融会社です。9年前、Jane Streetの誰一人としてOCamlの名前を聞いたことがありませんでした。いまや、JaneStreetはOCamlの最大の企業ユーザです。どれほどかといえば、約200万行のOCamlコードがあり、日常業務でOCamlを使っている従業員が(全部で)65人います。おそらく、なにがOCamlをそれほどまでに効果的なツールにしているかを説明するには、どのように、そして何故OCamlへの転換が起きたかを説明するのが一番だと思います。それを理解するために、まずJane Streetで何が行われているかを理解する必要があります。
Jane Streetの本業は世界の電子化された金融市場に流動性を与えることです。それは本質的には仲介人をするということです。多くの有価証券の多くの取引に発注をし続けます。発注の1つ1つがある証券をある価格で売るか買うかの意志となっていて、全体でJane Streetが提供する市場の広告となっているわけです。これらの発注を通して、企業は売る必要がある人から買い、買う必要がある人に売り、売買の価格の差で利益を得るわけです。常に同じような事をしようとしているプレーヤーとの競争になります。
電子的な流量を供給することは、技術的にとてもやり甲斐があります。それは必要とされる計算リソース(莫大な量のデータが消費され、解析され、その結果がリアルタイムに返されます)だけでなく、エンタープライズの複雑さ、という点でもです。--取引は複数の取引所、強制力のある団体、セキュリティ階層、タイムゾーンをまたがります。結果として出来てしまう複雑さを管理することは、ソフトウェアに大幅な投資を必要とする、とても手ごわいタスクです。
これら全てに関わる技術はリスクをもたらします。どの程度かといえば、取引会社が自爆する方法では、タイトループで残念な選択を何度も何度もし続けるトレードソフトをデプロイするのが最も速い、といえば分かるでしょうか。これらの技術的リスクに対してのJane Streetの反応として、容易に理解出来るソフトウェア、言い換えれば可読性が高いソフトウェアを作る事に非常に強く重点を置く、というものがありました。
コードを読むというのは、OCamlの最初の一行を書き始める前に会社がとったリスクに対するアプローチの一つでした。初期の段階から、(創始者を含む)何人かのシニアトレーダーが核となる取引システムに追加されるコードを、それがプロダクションになる前に、一行一行読んでいました。これは莫大な時間の投資であり、技術リスクに対する高い懸念と反映したものでした。
私がJane Streetに入ったのはPh.Dを取った年でした。ポスドクをしている間はパートタイムで働いていました。Jane Streetでの仕事は統計的解析やトレーディング戦略の最適化を中心にしていて、OCamlは解析を行う上での主要ツールでした。なぜOCamlなのか。私は修士課程でOCamlを学び、この言語に恋に落ちました。そしてOCamlはこのような素早くプロトタイプを作るような仕事にとても適しています。効率が非常に高く、それでいてC, C++, Javaと比較して速くエラーを起こしにくいです。私はJane Streetに在籍する期間は短く、自分が書いているコードはすべて捨てられると確信していたので、他人が自分のコードを後で使えるかどうかという心配を全くすることなく自分の生産性を最大化する選択をしました。6ヶ月で80,000行のコードができ、私は間違っていたと気づきました。気がつけばJane Streetでフルタイムの職に就いていました。そしてすぐに研究グループを作るための採用を始めました。
このとき、会社はソフトウェア開発で新しい手法に方向転換しようとしていました。会社の業務を回していたシステムははじめの数年は主にVBAとC#で書かれていました。実際コアとなる取引システム自体はとんでもない量の独自VBAが追加されたExcelのスプレッドシートでした。これは立ち上げてぱっと走らせるには良かったのですが、持続できる手法ではないことは明らかでした。
2003年、Jane Streetはコア取引システムをJavaで書き換えました。ここで書き換えたコードは結局破棄されました。その理由の一部としては、コードの可読性が下がりすぎて、状態を判断するにも難しくなりすぎたということが挙げられます。--実際置き換えられようとしているVBAよりも難しくなってしまったのですから。この原因の大部分はJavaの冗長性にあります。しかし冗長性だけにはとどまりません。VBAのコードは単純明快で真っ直ぐなコードなためかなり読み進めやすいです。しかしJavaでコードを書くと、どういうわけかクラスが入れ子状態のものが出来上がって、ただどのコード片があるメソッドが呼ばれたときに実際に何が呼び出されているかを理解するだけのために人々が頭をかきむしるようなものとなってしまいます。継承を多く使うようなコードは特に考えるのが難しいです。その理由の一部は、継承は抽象化という境界の下に潜ってしまうからです。
2005年、研究グループの成功が励みとなって、Jane Streetは再度コア取引システムを書き換え始めました。今度はOCamlでした。最初のプロトタイプは3ヶ月で行われました。そして取引を始めるまで更に3ヶ月かかりました。それから社内でのOCamlの使用はただ拡大していきました。今日では、OCamlは社内での問題解決のあらゆる部分に使われています。会計からシステム管理まで、あらゆるところで。そしてそういった努力は成長し続けています。ここ数年、会社の取引がOCamlの使用を拡大し、OCamlのトレーニングが今や新しい取引での雇用者のカリキュラムの標準となっています。結果として、OCamlへの移行は大成功で、他のどのやり方で得られたものよりもずっと強い技術を得ることが出来たのです。
なぜOCamlなのか?
とてもうまくいく言語というのに必要なものはなんでしょうか?ここに、私がOCamlのキーとなる強みとして気づいたところを短くまとめてみました。
- 簡潔さ: 私たちが研究の中でOCamlを使ってきた経験からすると、OCamlを使えば、JavaやC#のような言語で作るよりも、小さくて、簡潔で、理解しやすいシステムを作ることができると確信しています。可読性を重要視する組織であれば、これは大きな勝利です。
- バグの発見: OCaml未経験だったプログラマは型システムがバグを捕まえる程度にしばしば驚かされます。その時に得られる印象といったら、なんとか型チェッカーからコードに対して正しいという判定をもらったら、もうバグは残ってないと思うくらいです。もちろん実際はそうは行きません。OCamlの型システムは多くのバグに対して役に立ちません。しかしながら、テストではかなり見つけにくいようなバグも含め、型システムが効果的なバグというのは驚くほどたくさんあります。
- パフォーマンス: OCamlのパフォーマンスはJavaと同等あるいはそれよりも上で、CやC++に手の届く距離にいるとわかりました。高品質なコードジェネレータがあることに加えて、OCamlにはインクリメンタルGC(ガベージコレクタ)があります。これはGCが小さな仕事の塊を1度に片付けるようにチューニングすることができて、電子取引のようなソフトリアルタイムアプリケーションにより適した形にできるということです。
- 純粋、たいていは: 関数型プログラマはよく話に挙げるにも関わらず、mutable状態はプログラミングの基本的な部分であり、それを取り去ることはできないし、取り去るべきではありません。ネットワークパケットを送ったり、ディスクに書き込んだりするというのはmutableの例です。immutableに完全に取り組むということは、本物を決して作らないということです。しかしながらmutable状態はそれ自体がコストとなります。破壊的な操作が一切ないコードは一般的に読み解くのが簡単で、コード内でのやり取りや依存関係を明確で、管理が楽です。そういう意味でOCamlはいいバランスを保っています。破壊的操作は容易にしていますが、immutableなデータ構造を既定としています。上手く書かれたOCamlシステムはほとんどがmutable状態を持っていますが、その状態は注意深く制限されています。
おそらく最も簡単にこれらの利点を具体的にデモするには、簡潔性をお見せするのがいいでしょう。簡潔性において大事なことは曖昧さがないことです。言い換えればすなわち、短いコードは読みやすく、書きやすく、維持管理しやすいということです。もちろん限界はあります。すべての関数名を1文字にするなんていうのはだめです。とはいえ短いということは重要です。OCamlにはコードを小さくする仕組みがたくさんあります。
OCamlの利点の一つとして挙げられるのは型推論です。これによって型宣言をする必要性を取り除きます。これによってPythonやRubyのような動的型付け言語で書く時とほぼ同じくらい短くコードを書くことができます。同時に、静的型付けのパフォーマンスや正確さを享受することもできます。次のようなタプルを変形するOCamlの関数mapを考えましょう。
let map f (x,y,z) = (f x, f y, f z)
ここでmapは2つの引数を持つ関数と定義されています。関数fと3要素タプル (x,y,z) です。f xは関数fをxに適用するという構文です。ではC# 4.0ではどうなるか見てみましょう。C#のコードは機能的には等価ですが、見た目は雑然としていて、実際の構造が構文のノイズによって不明瞭になっています。
Tuple<U,U,U> Map<T,U>(Func <T,U> f, Tuple<T,T,T> t) { return new Tuple<U,U,U>(f(t.item1), f(t.item2), f(t.item3)); }
簡潔性の他の要因としてはOCamlにある型の表現の表記にあります。この表記の中心にあるのは代数データ型です。代数データ型とは積または和で新しい型を作ることが出来るシステム内で得られるものです。
直積型は2つの中ではよく知られている方です。タプル、レコード、構造体、オブジェクトはすべて直積型の例です。直積型は異なる型の複数の値を1つの値に結合します。これらは数学的に成分のデカルト積に対応するので直積型と呼ばれています。
直和型は成分の直和に対応して、複数の可能性を表すのに使われます。直和型が使われるのは複数のもの(aとbとc)を同時に表現したい時に使われます。直和型は(不格好ではあるけど)Javaでサブクラスを使うといった具合に、オブジェクト指向でモデル化出来ます。またCでは共用体として登場します。しかしほとんどの言語で直和型を安全に扱う上での型システムにおけるサポートは、驚くほど弱いのです。
次のコードは動作する代数データ型の例です。このコードは基礎述部のセットの上に成り立つBoolean式を表す型と、それらの式を評価する関数を定義しています。コードは基礎述部のセット上では一般的なので、これらの式の主部は整数の違いから、コンパイラフラグの設定まで、何にでも使えます。
- OCamlでの表現型と評価器
type 'a expr = | True | False | And of 'a expr * 'a expr | Or of 'a expr * 'a expr | Not of 'a expr | Base of 'a let rec eval eval_base expr = let eval' x = eval eval_base x in match expr with | True -> true | False -> false | Base base -> eval_base base | And (x,y) -> eval' x && eval' y | Or (x,y) -> eval' x || eval' y | Not x -> not (eval' x)
直和型の式は異なる宣言部を分けているパイプで示されています。これらの宣言部の内、たとえばTrueやFalseは単一タグで、JavaやCの列挙型の要素と実質的には変わりません。他のもの、たとえばAndやNotは結びついているデータがあって、そのデータは場合によって変わります。この型はAndやOrの部分がタプルを含んでいるので、実際直和型と直積型の両方を含んでいます。積と和の重構造の組み合わせで成り立つ型はOCamlではよくあるもので、強力なイディオムです。
注目すべき構文としては型変数 'a があります。型変数はどんな型でもインスタンス化でき、これによって基本述部のセットの上でコードをジェネリックにできるのです。これはジェネリック型のJavaやC#での扱われ方と似ています。すなわち、Javaの <A>List はOCamlでは 'a list と表現されます。
関数 eval は2つの引数を取ります。評価される式 expr と基本述部を評価する関数 eval_base です。コードはevalはあらゆる型の基本述部上の式に対して使うことが出来るという意味でジェネリックですが、 eval_base は基本述部の審議を評価するために必ず与えられなければいけません。関数 eval' は引数 eval_base で eval するための再帰呼び出しを簡単にするために定義されました。最終的にmatch文は考えられる式構造の条件解析と基本述部を評価するときの eval_base の呼び出し、そしてそれ以外の場合にデータ型の構造に対して再帰として動作するために使われます。
次のコードは同じコードをJavaで書いた場合です。冗長なのがまず目立ちます。Andのような1つの条件を追加するときに、OCamlでは2行(しかも短い)なのに、Javaでは8行です。--そしてこのJavaのコードは動作するものとしてはかなり最小にしています。もしクラス定義まで落としこまれていないこの表現型に対して、ほかのアルゴリズムを作らせようとした場合、ビジターパターンを使いたくなるでしょう。その場合行数は凄まじく膨らみます。
- Javaでの表現型と評価器
public abstract class Expr<T> { public interface Evaluator<T> { boolean evaluate(T value); } public abstract boolean eval(Evaluator<T> evaluator); public class True<T> extends Expr<T> { public boolean eval(Evaluator<T> evaluator) { return true; } } public class False<T> extends Expr<T> { public boolean eval(Evaluator<T> evaluator) { return false; } } public class Base<T> extends Expr<T> { public final T value; public Base(T value) { this.value = value; } public boolean eval(Evaluator<T> evaluator) { return evaluator.evaluate(value); } } public class And<T> extends Expr<T> { public final Expr<T> expr1; public final Expr<T> expr2; public And(Expr<T> expr1, Expr<T> expr2) { this.expr1 = expr1; this.expr2 = expr2; } public boolean eval(Evaluator<T> evaluator) { return expr1.eval(evaluator) && expr2.eval(evaluator); } } public class Or<T> extends Expr<T> { public final Expr<T> expr1; public final Expr<T> expr2; public Or(Expr<T> expr1, Expr<T> expr2) { this.expr1 = expr1; this.expr2 = expr2; } public boolean eval(Evaluator<T> evaluator) { return expr1.eval(evaluator) || expr2.eval(evaluator); } } public class Not<T> extends Expr<T> { public final Expr<T> expr; public Not(Expr<T> expr) { this.expr = expr; } public boolean eval(Evaluator<T> evaluator) { return !expr.eval(evaluator); } } }
言語の他の側面でもっと説明を必要とする部分としては型システムがバグを捕まえる能力があります。OCamlや関連した言語(あるいはそれにに詳しい人)をよく知らない人はしばしば型システムの力を誤って低く見積もってしまいます。型システムがすることは、変数に正しく渡していることを確実にすることだけだ(たとえば、floatを想定しているところにfloatを入れる)と結論付けるのは簡単です。
しかしそういうだけ以上のものがあります。純粋に型システムを使うだけでも恐ろしいほどバグを捕まえます。次のリスト内の重複を削除するPythonコードを考えてみましょう。
# Removes sequential duplicates, e.g., # destutter([1,1,4,3,3,2]) = [1,4,3,2] def destutter(list): l = [] for i in range(len(list)): if list[i] != list[i+1]: l.append(list[i]) return l
このコードはとても直線的ですが、バグがあります。このコードはリストの末尾を上手く扱えません。これを直してみましょう:
def destutter(list): l = [] for i in range(len(list)): if i + 1 >= len(list) or list[i] != list[i+1]: l.append(list[i]) return l
ここでOCamlで同じような関数を書いたら同じようなバグが起きるか見てみましょう:
let rec destutter l = match l with | [] -> [] | x :: y :: rest -> if x = y then destutter (y :: rest) else x :: destutter (y :: rest)
このコードはリストの要素にアクセスするためにOCamlのパターンマッチ構文を使っています。ここで :: はリストのコンストラクタで、 [] は空のリストを表しています。したがって、 [] の場合は空のリストに対応して、 x::y::rest の場合は少なくとも2つの要素を持つリストに対応します。変数 rest は残りのリスト(空のこともある)を参照しています。
Pythonでの例のようにこのコードが失敗して、リストの最後まで来て要素が1つしか残っていない場合に何が起きるかを考えてみましょう。この場合は、実行時ではなくコンパイル時に問題を検出できます。コンパイラは次のようなエラーを表示します:
File "destutter.ml", line 2, characters 2-125: Warning 8: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: _::[]
忘れている条件は _::[] で、リストに1つしか要素がない場合です。
コードを直し(そしてコンパイラに怒られないようにし)てあげるには、忘れている条件を追加してあげればいいのです:
let rec destutter l = match l with | [] -> [] | x :: [] -> x :: [] | x :: y :: rest -> if x = y then destutter (y :: rest) else x :: destutter (y :: rest)
ここで扱ったエラーはテストをすれば簡単に見つかるような些細なものです。しかし、型システムはテストがなかなかできないようなエラーも同様に見つけ出します。たとえばテストでは見落としやすい非常に稀にしか現れない問題や、モックアップや実例を作るのが非常に難しい複雑なシステムに現れる問題などです。
OCamlはインストールしてすぐの状態ですでにバグを捕まえることに非常に長けていますが、型を慎重に設計すればさらにその能力を発揮することができます。ネットワークコネクションの状態を表している次のような型を例として考えてみて下さい:
type connection_state = | Connecting | Connected | Disconnected type connection_info = { state: connection_state; server: inet_addr; last_ping_time: time option; last_ping_id: int option; session_id: string option; when_initiated: time option; when_disconnected: time option; }
connection_state 型はコネクションが成りうる3つの状態に名前をつけただけの単純な列挙型です。connection_infoはコネクションの異なる側面を表現するフィールドをいくつも含んだレコード型です。型の最後に option と付いているフィールドは本質的にはヌルを許可するフィールドです。(デフォルトで、OCamlの値はヌルではないことが保証されています)それ以外にはこのコードはJavaやC#で書いた場合と何も違いはありません。
次にレコードの個々のフィールド同士の関係を記述します:
- server はコネクションの相手方のサーバを識別するものです。
- last_ping_time と last_ping_id は keep-alive プロトコルの一部として使われます。これらのフィールドの両方が存在するか、両方共存在しないかのどちらかであることに気をつけてください。またこれらは state が isConnected の時にのみ存在します。
- session_id はコネクションが確立される度に新しく選ばれる一意識別子です。これも state が isConnected の時のみ存在します。
- when_initiated はいつコネクションを確立しようとしたかを記録するためにあります。これはコネクションの試行をいつ破棄するかを決定するために使われます。これは state が isConnecting の時にのみ存在すべきです。
- when_disconnected はいつコネクションが Disconnected の状態になったかを記録します。そしてその状態のときにのみ存在すべきです。
ご覧のとおり、多くの不変条件が異なるフィールドに結び付けられています。これらの不変条件を管理することが本当の仕事です。あとで訳が分からなくならないように、慎重に文書化する必要があります。これらの不変条件を検証するためにテストを書く必要があります。そしてコードが進化するに連れて不変条件を破ってしまわないように警戒を続けなければいけません。しかしそれも上手くやれます。次の様に書き換えるのです:
type connecting = { when_initiated: time; } type connected = { last_ping : (time * int) option; session_id: string; } type disconnected = { when_disconnected: time; } type connection_state = | Connecting of connecting | Connected of connected | Disconnected of disconnected type connection_info = { state: connection_state; server: inet_addr; }
これで直積型と直和型の組み合わせとなって、より正確にコネクションの取りうる状態を表現できるようになりました。特に、3つの状態に対して異なるレコード型を用意しています。それぞれはその状態に対してのみ関係がある情報を含んでいます。常に関係がある情報(この場合はserverだけ)は最上位のレコードに移されています。また、 last_ping_time と last_ping_id は両方共存在するか、あるいは両方共存在しないかを last_ping 飲みを用意することで明示的にしました。これはオプショナルなペアになっています。
これらをすべて行うことで、多くの必要な不変条件を型の中に埋め込みました。いまや不変条件は型の一部なのです。コンパイラはこれらの不変条件を破るコードを見つけ、拒否することができます。これは手作業で行う仕事を減らし、手で管理するよりも信頼できます。
この例は不変条件を実装するために代数データ型を使いました。しかしOCamlは同じ事をするのに別の道具も用意しています。OCamlのモジュールシステムはその一例です。不変条件をモジュールのインターフェースとして定義できます。大抵のオブジェクト指向言語とは違って、OCamlでは複雑に組み合わさった不変条件を複数の異なる型で表現することができます。より一般的に言えば、OCamlのモジュールはコードを小さく、理解しやすいパーツに落としこむ強力な道具であり、これらのパーツ同士のやり取りはプログラマが明示的に制御できるようになるのです。
型システムがバグを捕まえる能力は単独の小さなプロジェクトでも価値がありますが、真価を発揮するのは複数の開発者が一緒に生存期間が長いコードベースを共有するような協調環境においてです。バグを見つけるだけでなく、型シグネチャは正しいことが保証されたドキュメントとして驚くほど価値ある役割を担っています。進化するコードベースの中では型システムで強化された不変条件は規約によって管理されているものよりもずっと耐久性が高いという利点があります。その結果他の開発者によって誤って壊されるということが少ないです。
制限
この文書はOCamlには欠点がない、と言おうとしているわけではありません。もちろん、マイナー言語であることに起因する問題はすべてあります。OCamlには豊富なライブラリ群を生み出す素晴らしいコミュニティがありますが、それらライブラリ群もPythonやCやJavaにあるものと比べると見劣りします。同様にIDE、プロファイラ、デバッガのような開発ツールもありますが、メインストリームの言語と比べると成熟度合いと機能が著しく不足しています。
OCamlの他の制限で並行性を挙げなければいけません。OCamlのランタイムには単一ランタイムロックがあります。これはつまり、1台のマシンにある複数コアの恩恵を預かるにはマルチプロセスを使わなければいけないということです。大抵の場合、これは我々の開発モデルに適応します。並行性のプログラミングモデルとして共有メモリのスレッドを使うよりもメッセージパッシングを好むので、この特徴は読みやすいコードにつながり、複数の物理マシンの間でスケールアウトしやすいコードとなります。広くOCaml界でこのようなマルチプロセスプログラミングをするツールが手に入ります。しかし、まだ未成熟なのです。
しかしOCamlの制限は全く根本的なものではありません。それは実装方法や言語の人気に関係するもので、言語そのものにおけるものではないのです。終わりになりますが、それが私が最も困ったことだと思ったことなのです。いまや私はOCamlの背景にあるコアなアイディアというのは非常に価値があると確信していて、それはOCamlそれ自身が、たとえどんな制限があろうとも、非常に効果的で強力な道具だという事実に裏打ちされています。しかし、これらのアイディアは依然としてメインストリームの外にあります。
おそらくこれは変化の境界にあるのだと思います。F#やScalaのような言語は、それぞれ.NetやJavaのエコシステムと統合することで、OCamlやHaskellの背景にあるアイディアを大衆にもたらしています。おそらく10年後には、なぜこれらのアイディアは大衆に受け入れられなかったかなどと質問する必要はなくなるでしょう。しかし何も10年待つ必要はないのです。OCamlをあなたの道具箱に今追加すればいいのです。
原著者の許可
from: Yaron Minsky
to: Yoshifumi YAMAGUCHI
date: Thu, Nov 10, 2011 at 12:40 PM
subject: Re: Request for translation of your articleI'm really glad that you translated the article, but I don't know if I can personally approve it --- I think the ACM has some rights to the article now that it's been published in Queue, so if you want formal approval you'll have to ask them.
But I certainly have no objections. Thanks for doing it!
y
On Wed, Nov 9, 2011 at 9:40 PM, Yoshifumi YAMAGUCHI
wrote:
> Hi Yaron,
>
> Nice to meet you.
> I'm Yoshi who is interested in OCaml and I've been affected with your article, "Why the next language you learn should be functional".
> http://queue.acm.org/detail.cfm?id=2038036
>
> Though it is retrospective approval, I'd like to receive it from you for translating the article > into Japanese to introduce your impressive article to Japanese engineers.
> http://d.hatena.ne.jp/ymotongpoo/20111105/1320506449
>
> Would you approve it?
>
> I sent this message to you company e-mail address, sorry for sudden and rude request.
> I consulted Jun Furuse for my translation and he said my interpretation is acceptable.
>
> Cheers,
> Yoshi
> ----------
> Yoshifumi YAMAGUCHI
> @ymotongpoo
> http://www.google.com/profiles/ymotongpoo
*1:id:camlspotter 指摘有難うございました