RDFa Liteのここが酷い

RDFa LiteはRDFa Coreのサブセットで、とても短い仕様なのだけれども、一点気に入らない。

なぜ、@aboutではなく@resourceを採用したのか。

RDFa 1.1 Primerによれば、@aboutと@resourceの違いは、同じ要素に@propertyが設定されている場合の意味の差だけだ。どう違うかというと……

  • @aboutは、いつでも関係の主体 (subject) を設定する。
  • @resourceは、@propertyが設定されていない場合は@aboutと同様関係の主体を設定する。@propertyが設定されている場合は@hrefと同様関係の客体 (resource object) を設定する。(ただし、hrefと違って、クリックできないリンクになる。

属性名からも明らかなように、RDFa Coreの@resourceはリソースオブジェクトを設定するのが主用途だ。@propertyが設定されていない場合の意味は、「ついで」で決められていることに過ぎない。*1(これ自体センスがない仕様だ。意味が無い指定であれば未定義として残しておけばいいのであって、無理やり@aboutと同義にする必要がない。)

@resourceの唯一の意義であった「@hrefと違って、クリックできないリンクになる」という性質だって、今日のHTMLの仕様と組み合わせるのであれば、a要素の代わりにlink要素を使えば達成できるのだから、いよいよ@resourceの存在意義は無い。

@aboutを捨てて@resourceの方を選ぶというのは、全くセンスがない。他の属性が設定されているかどうかによって意味が変わる属性とか、ありえない。@propertyと@resourceを同じ要素に設定する“間違い”をしでかす人間は必ず出てくるし、仕様を策定する人間にセンスがないと、全世界に迷惑を掛ける。RDFa 1.1 Primerにその“間違い”を例示するぐらいだったら、最初から@aboutをプライマリとして採用してくださいよ。

*1:追記: subject、object両方とも、URIで指定されるのはresourceであり、resourceという名前がobjectを指しているという指摘は不当なので取り消す。しかし、場合によってsubjectの指定であったりobjectの指定であったりすることの筋の悪さに変わりはない。

schema.org/URLってダメ

Poor schema.org/URL - Ryusei’s Notes (a.k.a. M59のブログ)の雑な日本語訳。というか、原文が雑な英語すぎるし、まとまってない。)

schema.org/URL というデータ型が定義されているんだけど、これって酷いよね。「この属性のデータ型って何?」「URL。」って、答えになってないじゃん。

URLってのは、単なる識別子だよ。これはURLだなんて分かってる。ウェブにあるモノってのは、だいたいそれを指すURLを持ってる。知りたいのは、それがURLってことじゃなくて、そのURLが指しているモノの型が何なのかってことなんだから。

URLは、schema.org/Textのサブタイプであるべきじゃない。ハイパーリンクとして指定されているURLは、モノがある場所として解釈されるべきで、URLそれ自体がモノとして解釈されるべきではない。

Poor schema.org/URL

schema.org/URL is one of data types defined for linked data. I hate it, because it is totally useless. “Hey, what is the data type of this attribute?” “URL.” That is nonsense.

An URL is just an identifier. That is URL, I know. Almost every thing on the web have an URL that denotes it. What I want to know is the data type of the thing that the URL stands for.

URL should not the subtype of schema.org/Text. The URL specified for a property as hyperlink should be interpreted as the location that the thing is. Not be the URL itself the thing.

ソースコードは基本設計図か

www.nikkei.com

用語ミニ解説として、オープンソースが「基本設計図である「ソースコード」が公に開示されているソフト」と解説されているのだけれども、ソースコードは基本設計図なのだろうか。

基本設計図というのは建築における用語だ。建築では、設計図には基本設計図と実施設計図とがある。建築工事に必要なのは、実施設計図だ。www.homes.co.jp

建築における基本設計図とソフトウェアにおけるソースコードの間には、重大な差異がある。基本設計図の段階ではまだ建物は建てられないのに対して、ソースコードは既にビルドできる状態のもので、ビルドすれば実働するソフトウェアが得られるという点だ。建築にたとえるなら、ソースコードはすでに「建築工事」に回せる段階のものだから、これはむしろ「実施設計図」に相当する。

コメント欄についてのコメントまとめ

Twitterのツイートをまとめました。

2012年


2014年1月17日

NATROMの日記のコメント欄が承認制になった時のコメントです。











2015年1月6日

Qiitaというウェブサイトのコメント欄が荒れた時のコメントです。

2015年7月28日

またQiitaのコメント欄が荒れていました。その時のコメントです。






2015年8月5日

コメント欄について議論する分野はどこなのかについての疑問です。



望ましいコメント欄の姿はなんだろう。

この記事は、イケダハヤト氏の記事に反応して書かれたものだ。コメント欄について、私は過去にもいくらか考えてきた。Twilogからコメント欄についてのコメントを拾い、あらためて自分の意見を確認した。

www.ikedahayato.com

この記事ではコメント欄を閉鎖する理由を「ゴミの掃き溜めです」、「建設的ではありません」と、説明している。一方で、「サイトコンテンツを拡充するという観点では、コメント欄は本来的には有意義」とも書いている。前者は、コメント欄につくコメントは無価値であると言っていて、しかし、後者はコメント欄が本来的に有価値であることを訴えている。

私もブログのコメント欄は閉鎖したほうがよい場合があると考えているが、それはコメント欄にゴミのようなコメントしか付かないから、議論が建設的でないから、といった理由ではない。だいたい「ゴミのような」だとか「建設的でない」のような判断は主観的な価値判断に過ぎないもので、ゴミのようなコメントが付いたり、建設的でない議論が繰り返されること自体から、何らかの意味を見出すこともできる。ゴミのようなコメントが付くコメント欄は十分有意義で、ただ記事の著者にとっては不都合・不愉快である場合があるというだけだ。

私が撤去したほうが良いと考えるコメント欄には、個々のコメントの管理の問題と、話題の管理の問題がある。

個々のコメント管理の問題というのは、社会的に問題のある発言を削除しなければならないということだ。もしコメント欄の管理を記事の著者自身で行っていると、コメント投稿者に、記事の著者にとって都合の悪い発言や反論が不当に削除・検閲されているのではないかという疑念を抱かせてしまう。最初から外部のサービスにコメント管理を委譲してしまい、著者自身で管理できないようにしてしまえば、著者は他人のコメントに何ら責任を負う必要がなく、とても気楽だ。

話題の管理の問題というのは、コメント欄が荒れることだ。はてなブログのコメント欄のように、コメントが一列のリストになっている場合、コメント欄の荒れが問題になりやすく、荒らしに脆弱だ。コメント欄が荒れて機能不全を起こすのは「記事に対するコメント」と「コメントに対するコメント」が区別されていないからで、スラドのようにツリー構造のコメント欄であれば、ツリーごとに異なる話題を続けられるから、ひとつの枝が荒れていても、他の枝に影響しづらい。あるいは、ツイッターのように、コメントどうしが勝手に参照しあうような構造でもよい。ツイッターのような構造は議論には向かなくなり、よりコメントらしい使い方になる。はてブのように、コメント量を制限しても、荒れることはない。

そういうわけで、撤去せずとも良いコメント欄は次の性質を持つ。

  • 著者自身が管理できない。個別のコメントの削除はコメントした人自身か、コメントサービス提供者が行う。記事の著者が他人のコメントに責任を負ってはいけない。
  • コメント欄の話題が1つにロックされない。複数の話題を持てるツリー型や、話題が存在しないツイート収集型、コメント量を制限するブックマークコメント型などにする。

Facebookコメント、Twitterの検索結果タイムライン、そしてこのブログにあるような、はてブコメント欄のどれも、この性質を満たしている。あとはコメント欄をブログに埋め込むかどうかは個々人の“衛生観念”の差で決まるもので、自分に対して批判的なコメントを読むのは精神に悪いからコメント欄を設置しないというのも、別にいいのではないかと思う。

CSSでヒラギノ角ゴシックのウェイトを指定する方法

※追記あります

Mac OS X El Capitanのヒラギノ角ゴシック — Medium

ヒラギノ角ゴシックは10ウェイトあるが、CSSでは9つ(100から900まで100間隔)でしか指定できない。しかも、具体的にどのウェイトで表示されるかが、ブラウザによって異なっているようだ。

f:id:mandel59:20151003203853p:plain:h411

font-weightと実際に使われるウェイトの対応の表:

CSS font-weight Firefox Chrome
100 W0 W0
200 W2 W1
300 W3 W3
400 W4 W4
500 W5 W5
600 W6 W6
700 W7 W7
800 W8 W8
900 W9 W9

また、normalとboldはそれぞれ400と700を指定するのと同じなのだが、今までのヒラギノ角ゴではW3とW6を使ってきたのだから、これでは少し太いかもしれない。こういう時は、@font-faceを使って、ウェイトと実際に使われるフォントの関係を直接指定してしまえばいい。

@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W0);
  font-weight: 100;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W1);
  font-weight: 200;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W2);
  font-weight: 300;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W3);
  font-weight: 400;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W4);
  font-weight: 500;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W5);
  font-weight: 600;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W6);
  font-weight: 700;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W7);
  font-weight: 800;
}
@font-face {
  font-family: "Hiragino Sans";
  src: local(HiraginoSans-W8);
  font-weight: 900;
}
@font-face {
  font-family: "Hiragino Sans W9";
  src: local(HiraginoSans-W9);
  font-weight: 900;
}
div {
  font-family: "Hiragino Sans";
  font-size: 20pt;
  line-height: 1.5em;
}
<div style="font-weight: 100">ヒラギノ角ゴシック W0</div>
<div style="font-weight: 200">ヒラギノ角ゴシック W1</div>
<div style="font-weight: 300">ヒラギノ角ゴシック W2</div>
<div style="font-weight: 400">ヒラギノ角ゴシック W3</div>
<div style="font-weight: 500">ヒラギノ角ゴシック W4</div>
<div style="font-weight: 600">ヒラギノ角ゴシック W5</div>
<div style="font-weight: 700">ヒラギノ角ゴシック W6</div>
<div style="font-weight: 800">ヒラギノ角ゴシック W7</div>
<div style="font-weight: 900">ヒラギノ角ゴシック W8</div>
<div style="font-family: 'Hiragino Sans W9'">ヒラギノ角ゴシック W9</div>

f:id:mandel59:20151003204830p:plain:h410

10月4日追記

上の方法だと、Firefoxでは正しく表示されるが、SafariGoogle Chromeだと、W5が正しく表示されない。
f:id:mandel59:20151004131621p:plain:h406 Safariでの表示
本来boldでないW5にfont-weight: 600を指定していると、機械的にboldになってしまうようだ。
やはりfont-weightを使わず、直接指定するのが無難だろう。

指定例: A Pen by Captain Anonymous

GMailのリプライをスレッドにまとめる機能は完全にダメ

GMailのアプリは関連するメールを一つにまとめる機能があるけど、この仕様を把握していないせいで完全に失敗した。メールに新規の未読のリプライが付いても、それは既読のメールの下に表示されるわけで、これじゃあ未読のリプライの内容を読み逃してしまうに決まっているじゃないか。なんでこんな仕様なんだ。

f:id:mandel59:20151001142047p:plain

https://support.google.com/mail/answer/5900?hl=ja

人力検索はてなの質問をキャンセルしたら内容が消えてしまったので、こちらに残しておく。

昔読んだ数学の児童書を探しています。 内容は断片的にしか覚えていません。おそらく12年以上前に図書館で読んだ本です。 小説仕立ての本であり、ワープロフロッピーディスクに、奇妙な外字で書かれた文書が保存されていて、それはコンピューターに住む未知の生命体の残した、未知の数学に関するメッセージだったというような話だったように思います。いくつかのトピックがあったと思いますが、そのひとつとして、p進数(p-adic number)に関連するようなトピックがありました。もしかしたら、大きめの判型の本で、上下で2冊分の本だったかもしれませんが、確かではありません。 少ない情報ですが、心当たりがあるかたはよろしくお願いします。

http://q.hatena.ne.jp/1443281726

www.amazon.co.jp

www.amazon.co.jp

ありがとうございました。

関数とラムダ計算の話

bugrammer.hateblo.jp

この記事へのツッコミみたいなことを書くつもりだったけど、あんまり関係ない話になったので、サイドストーリーみたいにして読んでください。

分野によって「関数」が違うこともある

前回、数学的な関数の定義の話があったけど、あれは写像の定義であって、個々の分野で「関数」と呼ばれているものと、いつでも一致するとは限らない。まあ普通は関数と言ったらほとんどの場合写像を指すので、少し違う物を指すときは、多価関数とか連続関数みたいに、別の名前を付けていることも多い。

で、プログラミングに関する文脈で「関数」と呼ばれるものが写像と一致するかといえば、もちろん一致しない。ある程度似た部分もあるけど、違う性質を持っている。この違いを、数学的にどうやって説明するかが問題になる。計算の性質を説明するための抽象モデルが抽象書換系で、ラムダ計算の意味も、抽象書換系によって定義できる。この定義の方法は、写像の定義とは大きく異なっている。

写像とラムダ項の間には、具体的にどういう違うがあるのだろうか。

一方で、写像には右一意性\forall a \in A, \forall b_1, b_2 \in B, ((a, b_1) \in G_f \wedge (a, b_2) \in G_f \Rightarrow b_1 = b_2)という性質があるが、ラムダ計算には合流性という性質があり、計算が停止する範囲では、写像とよく似た性質を持っているということが言える。

ラムダ計算の派生

ラムダ計算には、簡約の方法を制限したり、型を付けたりするといった、色々な派生系が存在していて、それぞれの体系が異なる性質を持っている。例えば、単純型付きラムダ計算は、型なしラムダ計算と異なり、計算が必ず停止する。(強停止性を持つ。)

よく知られている関数型言語の多くは、ラムダ計算から派生した体系を使っているが、ラムダ計算以外の体系を使った関数型言語も考えられる。

アクションと関数、式の値の話

bugrammer.hateblo.jp

  • a -> bというのは、引数にa型の値をとり、b型の値を返す関数の型だ。
  • Monad mが存在するような任意の型コンストラクタmについて、m aというのは、a型の値を生成する(Monad mの)アクションの型だ。

という型の意味を理解していれば、関数(>>) :: Monad m => m a -> m b -> m bは、その型から、

  • Monad mが存在するような任意の型コンストラクタmについて、
    • m a型の値(アクション)(これをxとする)を引数にとり、
    • m b -> m b型の値(関数)(これは(>>) xと表せる)を返す関数で、つまりその返される関数は、
      • m b型の値(アクション)(これをyとする)を引数にとり、
      • m b型の値(アクション)(これは(>>) x yと表せる)を返す関数で、つまりその返されるアクションは、
        • b型の値を生成するアクション

だということが読み取れる。

一方で、「m a型の値(アクション)xを破棄する」といったことは、型から読み取ることはできないし、事実でもない。実際、(>>) x y = (>>=) x (\z -> y)と定義でき、アクションxは破棄されない。破棄されるのはアクションxが生成する値で、それはxではなくzだ。

関数が値を返すということと、アクションが値を生成するということには大差がないように見える。実際に、多くのプログラミング言語は関数に副作用を認めていて、関数とアクションを区別しない。しかし、関数が、引数が定まれば返り値も一意に定まるという性質を持っているのに対し、アクションは、生成する値が定まるという性質を持っていないから、同列に扱おうとすると、式の値が一意に決まらなくなる、という困ったことが起きる。

そもそも、式の値はなんだろうか。(ここで書いている「手続き型プログラミング」「関数型プログラミング」という分け方は一面的なものに過ぎないから、あんまり深刻に考えないで欲しいのだけれども、)手続き型プログラミングにおいて、値というのは手続きから手続きへ渡されたり、変数に代入されたり読み出されたりするものであって、「式が値を持つ」という考えは、算術の式に対しては考えていても、プログラム一般に敷衍されるものではないように思われる。プログラムは文の集合だ。文は意味を持つが値を持たない。それが、手続き的なプログラミングにおいて一般的な考え方だ。

一方、関数型プログラミングでは、プログラムは式だ。関数型プログラミング言語の式は部分式と関数適用から構成されていて、どの部分式も値を持つ。関数型プログラミングでは、プログラムの構造を内包した値を扱うことでプログラミングするようになる。直和型の値は分岐構造を内包しているし、リスト型の値は反復構造を内包している。モナドは、手続き型言語における各種副作用を持つ、逐次構造を内包した型の集まり(型クラス)ということになる。このように複雑な値を使うことで、関数型プログラミングではプログラムの意味と式の値を同一視できるようになる。

プログラムの意味と式の値を同一視できると何が嬉しいかというと、意味を持つプログラムの断片がすべて値を持つので、プログラムを処理するプログラムが書きやすくなる。プログラムのどの断片も、関数として抽象化することができる。他の関数に渡すことが出来る。変数にプログラムを代入できる。

話をアクションに戻す。Haskellにおいては、アクションが生成する値と、アクション自体は別だ。たとえば、getChar :: IO Charは標準入力から文字を1文字読み込み、その文字に対応するChar型の値を生成するアクションなのだけれども、Haskellでは、式getCharの値は「標準入力から文字を1文字読み込み、その文字に対応するChar型の値を生成するアクション」自体であり、生成されたChar型の値ではない。アクションgetCharの生成する値を取り出して言及するには、do {z <- getChar; k z}zgetChar >>= \z -> k zzとでも言うしかない。

色々書いたけど、結局、値とは何かという話に合意が得られていない状況ではどうにも話が通じないという点が問題で、以前からココらへんの考え方を整理して参照透過性についてなんとか説明しようと試みたりしているのだけれども、やっぱり難しいですね。

追記のメモ書き

ツッコミどころ

  • Haskellにも値を持たない、式より上位の宣言の構造があるじゃん
  • プログラムの意味と値が一致しない純粋でない関数型言語なんてザラでしょ
  • 値を返す手続きを第一級の値として持てれば、純粋でなくても高度なプログラミングが簡単にできる上に、手続きを取り扱うだけのためにモナドを取り入れる必要がない。プログラムの意味と値を一致させる必要ってないんじゃないのか。
  • モナド以外に、一意型を使って値の複製を制限し、制御(ハンドラ)を表す値を導入することで、プログラムの意味と値の一致を図る方法がある。

ES6のジェネレータはHaskellのdo記法ほど強力ではないという話

curiosity-driven.org
postd.cc

この記事では、ES6のジェネレータを使って、Haskellのdo記法を模倣したdoM関数を定義し、ジェネレータを使ってモナドを取り扱えることを示している。しかし “The same routine can be used with other monads like the Continuation monad” という記述に反し、実はdoMと一緒に使えるモナドと、使えないモナドがある。

話を簡単にするため、以下ではECMAScriptのプロトタイプチェーン機能を使わないでプログラムを書くことにし、doMには、モナドを表現するオブジェクトを、引数として別途渡すことにする。

function doM(m, gen) {
  function step(x) {
    var y = gen.next(x);
    if (y.done) {
      return y.value;
    }
    return m.bind(y.value, step);
  }
  return step();
}

具体的に、ふたつのモナドOMGMを定義する。OMはOptionMonadの略で、GMはGeneratorMonadの略だ。

var OM = {
  unit: function(x) {
    return {tag: "Some", value: x};
  },
  bind: function(n, k) {
    if (n.tag === "None") {
      return {tag: "None"};
    }
    return k(n.value);
  },
};

var GM = {
  unit: function*(x) {
    yield x;
  },
  bind: function*(g, k) {
    for (var i of g) {
      yield* k(i);
    }
  },
};

このとき、OMに対してはdoMを適用できる。

var x = doM(OM, function*() {
  var a = yield OM.unit(2);
  var b = yield OM.unit(3);
  return OM.unit(a * b);
}());

console.log(x); // ==> Object { tag: "Some", value: 6 }

var y = doM(OM, function*() {
  var a = yield OM.unit(2);
  var b = yield {tag: "None"};
  return OM.unit(a * b);
}());

console.log(y); // ==> Object { tag: "None" }

一方で、GMに対してdoMを使うと、例外が出てしまう。

var z = doM(GM, function*() {
  var a = yield function*(){ yield 2; yield 3; yield 4; }();
  var b = yield function*(){ yield 5; yield 6; }();
  return GeneratorMonad.unit(a * b);
}());

try {
  for(var i of z) {
   console.log(i);
  }
} catch (e) {
  console.log(e); // ==> TypeError: k(...) is undefined
}

なぜかといえば、GM.bindは第2引数k複数回呼び出す可能性があるからで、一方、doM内で定義されている関数stepは、ジェネレータの呼出しに副作用を使うことを前提としていて、複数回呼び出されることを考慮しない作りになっているという点が問題となっている。この問題を修正するには、途中まで実行したジェネレータを複製することができる必要があるが、ES6のジェネレータにそのような機能はないようなので、結局ジェネレータを使ってどのようなモナドにも対応したdoMを実装することはできないということになる。

比較用に、doMを使わない場合のコードも載せておく。

var w = GM.bind(function*(){ yield 2; yield 3; yield 4; }(), a =>
        GM.bind(function*(){ yield 5; yield 6; }(), b =>
        GM.unit(a * b)));

for(var i of w) {
  console.log(i);
}

「抽象化」を抽象化する 〜 nopは確かに抽象化のひとつの形であるという話のメモ書き

さて、以上の議論を踏まえた上で、「世界一抽象化されたコード」というのを今から書いてみましょう。一瞬でかけます。

# nop

上にあげたものがそうです。つまり、「なにもしない」というコードこそが、世界一抽象化されたコードです。

http://nekogata.hatenablog.com/entry/2015/08/05/001742

これが本当に抽象なのか? と疑問に思う人がいるのだけれども、これは確かに一種の抽象化ではあるのだ。これが抽象化だと思えないのは、このnopの例自体が、具体的にどういう抽象化を前提としているかを明示していない、抽象的な例だからだ。「抽象化」という概念それ自体が抽象的だ。関数は、機械に扱える抽象化の機構のひとつなのだけれども、だからといって関数だけが抽象化ではない。

抽象化というのは、プログラムを書くのに使える概念であるという以前に、物の考え方としての概念だという側面がある。このnopの例は、少なくとも、もはやRubyが形式化しているレベルの抽象を越えてしまっていて、それは計算機にとっては意味を持たない(具体的に計算できない)抽象化だ。Ruby処理系に# nopというコード自体を渡しても、これはコメントを含む空のプログラムだと具体的に解釈されるだけであって、全く抽象性を持たない。だけど、そういう、機械にとって意味を持たない抽象化でも、人間にとっては意味のある何かを抽象している場合がある。コメント行自体もそうだし、アルゴリズムの本などが載せている擬似コードなどは、機械が読めずとも人間が読める、抽象的なコードだ。その抽象的なコードは、人間が適宜解釈し、具体的なプログラミング言語で実装することができる。

条件分岐if C then A else Bを抽象化して、非決定性の分岐A ⊕ Bとして考える、といった話と似ている。(抽象化というより、近似と言ったほうがいいのかもしれないけど。あるいは、抽象化というのは、別の視点を変えれば、近似と見ることができる。)

普通プログラマが抽象化と言ったら、機械が扱える範囲での抽象化だけ考えているんだ、と言うかもしれないけど、どういう種類の抽象化を機械が扱えるかというのは、曖昧だ。擬似コードだって、十分に形式化されていれば、処理系を実装して実行することができる。

まとまらないけど、メモ書きだからこれで終わり。

CoffeeScriptで代数的データを表現する

関数型言語ではポピュラーな機能である、代数的データ型を愛してやまない人は多いだろう。しかし悲しいことに、CoffeeScriptは、代数的データ型を持っていない。今回は、オブジェクトを使って代数的データの条件分岐を表現する方法について書こうと思う。

次のコードは、数式の文字列化や評価を行うプログラムだ。

# 分岐関数
match = (x, c) -> x.apply(c)

# 構築子
num = (x) -> ->@num(x)
add = (x, y) -> ->@add(x, y)
mul = (x, y) -> ->@mul(x, y)

# 数式 1 + 2 * 3
exp = add(num(1), mul(num(2), num(3)))

# 文字列化
p = (exp) -> match exp,
  num: (x) -> String(x)
  add: (x, y) -> "(" + p(x) + ")+(" + p(y) + ")"
  mul: (x, y) -> "(" + p(x) + ")*(" + p(y) + ")"

# 評価
ev = (exp) -> match exp,
  num: (x) -> x
  add: (x, y) -> ev(x) + ev(y)
  mul: (x, y) -> ev(x) * ev(y)

console.log p(exp) + " = " + String(ev(exp))

実行すると、次のように表示される。

(1)+((2)*(3)) = 7

構築子

構築子は、次の形をしている。

c = (x...) -> ->@c(x...)

(仮引数列) -> 式は関数を作る構文、-> 式は引数のない関数をつくる構文で、@メソッド名(実引数列)JavaScriptthisメソッドを呼び出す。つまり構築子cは、this.cx...を引数として呼ぶクロージャーを返す関数だ。

分岐関数

条件分岐は関数matchに、代数的データと、分岐ごとの処理を表すオブジェクトを渡して行う。

分岐関数内では、渡された代数的データ(実体はクロージャー)をオブジェクトに適用している。

match = (x, c) -> x.apply(c)

こうすると、クロージャーがオブジェクトの適切なメソッドを呼び出し、条件分岐を行ってくれる。

もうひとつの方法

別の方法として、次のような分岐関数と構築子を使う方法が考えられる。

# 分岐関数
match = (x, c) -> c[x[0]].apply(c, x[1..])

# 数式の構築子
num = (x) -> ["num", x]
add = (x, y) -> ["add", x, y]
mul = (x, y) -> ["mul", x, y]

Debug the Haxe Compiler with OCamlDebug

1. Apply the following patch to the Makefile:

diff --git a/Makefile b/Makefile
index e912e15..1f6c71f 100644
--- a/Makefile
+++ b/Makefile
@@ -80,7 +80,7 @@ libs:
        make -C libs/objsize OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
 
 haxe: $(MODULES:=.$(MODULE_EXT))
-       $(COMPILER) -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(LIBS:=.$(LIB_EXT)) $(MODULES:=.$(MODULE_EXT))
+       $(COMPILER) -g -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(LIBS:=.$(LIB_EXT)) $(MODULES:=.$(MODULE_EXT))
 
 haxelib:
        (cd $(CURDIR)/extra/haxelib_src && $(CURDIR)/$(OUTPUT) haxelib.hxml && nekotools boot bin/haxelib.n)

2. Build the compiler with the following command:

make BYTECODE=1

3. Execute ocamldebug:

ocamldebug ./haxe [arguments]
% ocamldebug ./haxe
	OCaml Debugger version 4.02.1

(ocd) break @ Main 979
Loading program... done.
Breakpoint 1 at 3223236: file main.ml, line 980, characters 2-29726
(ocd) run
Time: 69442 - pc: 3223236 - module Main
Breakpoint: 1
980 	<|b|>let usage = Printf.sprintf
(ocd) n 20
Time: 69757 - pc: 3223608 - module Main
1000 	Common.define_value com Define.HaxeVer (float_repres (float_of_int version /. 1000.))<|a|>;
(ocd) p version
version: int = 3300
(ocd) rev
Time: 69442 - pc: 3223236 - module Main
Breakpoint: 1
980 	<|b|>let usage = Printf.sprintf
(ocd) prev
Time: 69441 - pc: 3198700 - module Main
637 			<|b|>init ctx;
(ocd)

You can even trace back to the past!

Check the debugger commands at The debugger (ocamldebug).

Happy hacking!