kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

山手線一周ウォーキング(二度目)をしてきました

一度目のときの記録

今回の記録

概要

一度目は2017なので、三年近く前のことになりますが、思い立って二度目をしてみることにしました。決意したのが昨日だったので、相方に「急にそんな運動をして大丈夫?」と心配されましたが…。なにはともあれ、昨日、二度目の山手線一周ウォーキングをしてきたのでした。

自分ルールは一度目のときと同じで、山手線を秋葉原から出発して、必ず各駅を通過して、写真をアップロードしてくること、です。

運動不足がたたったのもあり、最後の方はマジきつかったです。ジャージの上にコートを羽織っていったのですが、19:00くらいになると、寒いわ冷たい風が吹いてくるわで…。夜用に厚手のセーターとか用意しておけばよかったかもしれません。

しかし、足が限界ギリギリだったものの、10:30に出発して、23:00頃に無事、秋葉原まで戻ってくることができました。偉いぞ俺!と褒めてあげたい気持ちです。

2017のときは、秋葉原→神田→東京…というルートでしたが、今回は逆方向で、秋葉原御徒町→上野…というルートでいきました。これは、前回と変えた方が新鮮かなあと思ったのもありましたが、前回だと後半、アップダウンが激しかったり道がいりくねってたりして疲れたので、そっちを前半に回しちゃえ!みたいな理由だったりします。

感想

感想は色々ありますが、興味深かったり、面白かったことを列挙してみます。

  • 考え事がはかどる(重要)
  • 鶯谷周辺はやたらラブホがあるなあ
  • 品川以降は道がほとんどまっすぐで非常に歩きやすくて良い
  • 大崎周辺はいい感じのデートスポットがあった…がカップルが寒そうだったw
  • 色々、各駅の名物(?)とかあって、今度じっくり巡ってみたくなった

逆に、しんどかったこととしては、

  • 後半(16:00以降)、身体がしんどいが先に来て、考え事がはかどらなかった
  • 鶯谷巣鴨辺りは迷いまくりだった
  • 19:00以降、死ぬほど寒かった
  • 最後の方、足が棒だった

辺りでしょうか。凄い運動不足だったので、どうしても無理っぽそうだったら、途中で電車に乗って引き返すことも検討していましたが、そうならずに良かったです。

装備

真冬ですし、私は完膚なきまでに方向音痴ですので、色々準備していきました。

  • スマホ二台持ち
  • 大容量スマホバッテリー
    • 実際、途中でメイン機の方がバッテリー切れになったので、それがフル充電二回できるのは助かりました。
  • 断熱性高めのコート
    • これでも不十分でしたが…
  • タオル*2
    • 汗拭き用
  • アクエリアス*1
  • (追記)塩分補給手段

スマホ二台持ちは、特に良かったなあと思うところです。サブ機をGoogle Maps表示専用にしていたので、迷ったらすぐ見られましたし、メイン機のバッテリーを余計に食うこともなかったですし。ちなみに、サブ機の方がバッテリーの持ちが良く、13時間表示させ続けてましたが、半分くらいまでしかバッテリーが減ってませんでした。

あと、今日みたいに、非常にゆっくり歩くのなら、出発を1時間早めておけば、終電ぎりぎりで秋葉原から帰るというのを避けられたかもしません。

明日(今日)はきっと筋肉痛で養生してそうですけど、楽しかったです。皆さんにもお勧め…はしませんが、一周とは言わずとも、普段降りない駅を探検する旅も楽しいものだなと思ったのでした。

ダイエットします - 目標体重71kg

あけましておめでとうございます。1月1日ということで、きりがいいので、今年の目標の一つ(大きなもの)をまず宣言しておくことで、自分を追い込むメソッドを使ってみようと思います。

現在、私の身長は173cm、体重は83.4kg(今朝の起床時)。BMIは27辺りで、軽度肥満と判定されるレベルです。まあ、BMI自体はそれほど気にしていないのですが、去年の健康診断で

辺りが軒並み良くない値になっていて、生活習慣病状態です。ちなみに、酒はほとんど飲まないので、いわゆる非アルコール性脂肪肝ってやつです。中性脂肪の値なども高めです。

昨年後半あたりからダイエットを始めていて、ある程度効果は出たようなのですが、83kg~84kg辺りで体重が停滞しているのが最近の悩みです。糖質制限をしたりした結果、中性脂肪や肝機能の数値、LDLコレステロールには顕著な改善が見られたものの、体重はそこまで落ちていないように思います。

というわけで、ダイエットです。漫然とダイエットしていても仕方がないので、今年中の目標として、BMIに対する理想体重とされるよりもちょい上の70kgちょっとオーバーくらいに据えていく予定です。1か月1kgであれば、そこまで無理をせずにいけるはず…というのが目算ですが、果たしてうまく行くかどうか。 以下のようになっていれば理想です。

  • 2020年01月01日: 83kg
  • 2020年02月01日: 82kg
  • 2020年03月01日: 81kg
  • ...
  • 2021年01月01日: 71kg

現実には、漫然とダイエットしていると、停滞期が訪れるはずなので、この目標で頑張っても75kgくらいじゃないかなあと思ってますが、それでも大学1年の頃くらいにまで減るのでまあいいのではと思います。

運動はウォーキングとジョギングを増やす方向でいきますが(寒いとか調子悪いとやらないかも)、原料に支配的なのは食事ですし、基本は食事制限で行きます(去年後半もそうでした)。運動はさぼることはあっても、食事制限を継続します。

勉強会の懇親会や同僚との昼食あるいは夕食で、なんとなくしっかり食べてしまいそうな気がしますが、それ以外のものは基本的に質素にいきます。

甘いもの完全断ちとか欲望完全に絶つのは失敗するのは経験からわかりきっているので、

を買ってあります。甘いものが摂りたくなったら、これを水や炭酸水に混ぜるという戦略です。間食も完全制限とかすると、食欲抑えられなかった時に毒なので、1日1回はOKというところで行く予定(既にそんな感じ)です。ちなみに、現在の体重および過去1か月間での遷移はこんな感じです。あんまり減ってませんね(割と食べるの控えているはずなのですが)。

f:id:kmizushima:20200101083641p:plain
2020年1月時点での体重

1週間単位で振り返りをして、こつこつと減量していこうと思います。

もし、私が食事を食べ過ぎようとしていたら止めてやっていただけたら幸いです。

ではでは。

WORDIAN Advent Calendar 2019/12/22: プログラミング言語Klassicの紹介

まえがき

この記事はWORDIAN advent calenderの22日目の記事です。

以前にこのブログでも触れたことがあるのですが、私が開発中のプログラミング言語Klassicについて紹介します。

Klassicの紹介

リポジトリこちらScala環境があれば、ビルド&実行できますが、まだまだ挙動変わりまくってますので、試してみたいチャレンジャーな方々以外にはお勧めしません。

さて、Klassicです。そもそも、この言語、旧称Toysといって、探偵オペラミルキーホームズシリーズ[^milky] にハマってた頃に、そこから名前を取って作った言語だったりします。当時のリポジトリを見ると、二期が黒歴史にならなければいいとかいうのがサンプルコードに書いてあったりして、懐かしいなあと思ったりしました。

それはおいといて、Klassicです。

静的型&型推論を持っている

まずは例をば。

def myFoldLeft(list) = (z) => (f) => {
  if(isEmpty(list)) z else myFoldLeft(tail(list))(f(z, head(list)))(f)
}

これは、foldLeftを実装したコードです。Klassicは私の好みもあって、静的型を持っていますが、さらに、ML系が採用しているHindley-Milner型推論を実装しています。上のコードは型宣言が一切ありませんが、次のように型が推論されます。

def myFoldLeft<A, B>(list: List<A>) = (z: <B>) => (f: (B, A) => B) => B

ML系やHaskellとかだと当たり前のようにやってくれることですね。ただ、これまで、ちゃんとした型推論を自作言語に入れたことがなかったので、その実験も兼ねて入れてみました。Algorithm Wの変種っぽいもので、適当に単一化して、ごにょごにょやってます。

def add(x, y) = x + y

みたいなのの処理はどうするか悩んだのですが、Klassicにはまだ型クラスないし、ということで、型注釈がない場合+(Int, Int) => Intだと決め打ちすることにしました(ML風)。+が実は浮動小数点数にも使えるけど、辺りもML風。

OCamlにあるレコード多相ぽいもの、というか、いわゆるrow polymorphism的なものも実装してみました。

record P {
  x: Int
  y: Int
  z: Int
}
record Q {
  x: Double
  y: Double
  z: Double
}

record T <'a, 'b> {
  x: 'a
  y: 'b
}

def add_xy(o) = {
  o.x + o.y
}
assert(3 == add_xy(#P(1, 2, 3)))
assert(3 == add_xy(#T(1, 2)))

ここで、

add_xy: {o: { x: Int, y: Int, ...}): Int

て感じに推論されるのが味噌で、必要なメンバを持っていれば、継承とか関係なしに渡せます。これは、いわゆる構造的部分型とは異なる点に注意が必要です(多相型を用いて、サブタイピングのニーズの多くを満たす方式の一つが、row polymorphismだと私は認識してますが、この辺は識者からのツッコミをお待ちしています)。

こんな感じで、Klassicは、今のところ型注釈なしにだいたいのプログラムがうまく型付けされます(健全かどうかは証明できてないですが、健全だと思いたい)。

スペース&行センシティブなリテラル

これは、他の言語にはあまり見られない特徴かなーと思います。記憶が確かなら、たぶん、Fortress辺りに影響を受けて入れたものです。

たとえば、配列(リスト)のリテラルを表記するのに、

[1, 2, 3, 4, 5]

のように書くのは一般的ですが、ここでカンマが必要なのって、これってぶっちゃけパーザの都合ですよね。ちょっとトリッキーなパーザ作ってやればスペースのみで行けるんじゃね?と思って、パーザをごりごり書いてみたら、

[1 2 3 4 5]

という風に、カンマなしスペース区切りでいけるようになった、という話です。カンマを入れてもパーズしてくれますし、スペースの代わりに改行で要素を区切ることもできます。これ、どういうときに嬉しいかというと、たとえば、表をリテラルで表記したいときに、

val tbl = [
   [1 2 3]
   [4 5 6]
   [7 8 9]
] // tbl: List<List<Int>> と推論

という感じで、より表っぽく書けるのが利点かなと思います。なお、二次元配列のときには、レイアウトから決め打ちで要素をパーズできるようにしようかと考えたことがありましたが、その方式だと二次元だけ特別扱いになってビミョーなので、止めました。マップ、セット辺りも似たような感じで書けます。

val map = %[
  "k1" : "v1"
  "k2" : "v2" 
] // map: Map<String, String> と推論
val set = %(
    1 2
    3
) // set: Set<Int> と推論

リテラルの中に複雑な式を埋め込むことも(当然)できますが、割愛。

Java FFI

Klassicは現在、Scalaで実装されていますが、そのおかげで、簡単にJavaコードを呼び出せる機能を作ることができました。たとえば、以下のプログラムを評価すると、"F"になります。

"Foo"->substring(0, 1)

Javaの型をどうKlassicの型にマッピングするか悩みどころなのですが、今のところいわゆるプリミティブだけ特殊扱いで、それ以外のJavaの参照は*って特殊な型にしています。あんまりJVMべったりにしたくはないので、色々考え中です。

プレースホルダ構文

これは、思いっきりScalaのそれに影響を受けたものなのですが、

val xs = [1 2 3]
map(xs)(_ + 1)

て書くと、

val xs = [1 2 3]
map(xs)((x) => x + 1)

と展開されます(展開ルールが抽象構文依存な辺りもScalaの影響が強い)。

ちなみに、これは思っていなかったのですが、強力な型推論プレースホルダが組み合わさったことによって、Scalaでなんとなくプレースホルダ使っても型が付かなかった式にもうまく型がつくので、結構使い勝手がいいのでは、と自画自賛しています。

その他

Klassicはある意味で、私にとっての言語の実験場みたいなものなので、思いついた機能を次の日に実装してたりすることがよくあります。プレースホルダ構文もあ、Scala-thonで1日で作ったものだったりしますし。Lispは言語を拡張できるとはいえど、言語の見た目まで変えるにはリーダーマクロを持ち出さなきゃいけないわけで、言語の見た目を色々好き勝手にカスタマイズする実験をしたい私としては、手元にこういう実験のベースとなる言語があるのが性に合ってるなと思います。

今後の予定

Klassicは、0.1.0-alpha て形で、時々、区切りのいいところで、リリースをしています。ですが、masterブランチとはかなり乖離が激しいですし、今使ってもらうのは微妙な感じです(masterのビルドしてもらった方が早いかも)。この辺は、お手軽に試せるように環境(たとえば、Scala.jsを使ってブラウザ上で処理系が動くようにあトランスパイルするとか)を整えることを考えています。あと、現在のKlassicはインタプリタなのでめちゃ遅いですが、そろそろバイトコード出力とかLLVM IR出力とかやりたいところです。

現在、既に実用的に使うために最低限の言語機能は揃っているので、処理系のクオリティアップとかREPLサポートとかもやっていきたい感じです。もし興味があればお試しいただけると幸いです(感想も送ってくれるとさらに励みになります)。

ポモドーロ・テクニックぽいもの(仮)をできるだけ厳密に実践してみた

まとめ

  • ポモドーロっぽいもの(仮)を実践してみた
    • 我流なので、ポモドーロそのものじゃないよ
  • やり方をできるだけ厳密にした
    • 5分の休憩中は、モニターを見ない
    • 25分経ったら、何がなんでも休憩する
    • 同期割り込み(電話や郵便物)やトイレは例外扱い
  • 効果:
    • どの時間に何をやっていたのか、が可視化された
    • 強制的に休憩させられるので、集中力が長続きした(主観)
    • 時間に追われる感覚がかなり減った
  • 課題:
    • 栄養補給とかはどういう枠組みでとらえるのか
    • オフィスだとそのままは実践しづらい気がする(今日は自宅作業)
    • 一時的に疲労感が高まったときの扱い(強制ストップ?)
    • ...

動機

私は割とすぐに集中する(してしまう)タイプなので、集中することは比較的容易なのですが、一方で、集中を中断するのがかなり苦手です。そのため、途中で切り替えて別に作業をしたりするのがうまくなかったり、集中し過ぎて疲労感がたまるといったことに悩まされていました。

その対処として、ポモドーロ的な、30分を単位として、25分の作業と5分間の休憩を「強制的にとる」手法が使えるのではないかと考えたのがきっかけです。集中を中断するための手法を実験したかっただけで、ポモドーロ・テクニックの5段階とか、その他思想的な部分はあんまり受け継いでませんが。

実験条件

  • 場所:自宅
    • 週1でリモート作業が許されているため
  • 時間:14:15-19:50
  • 環境:
    • トリプルディスプレイ(32inch +28inch + 13inch)
      • うち、13inchの一つはポモドーロタイマー専用
    • Tomatoesをタイマーとして利用
  • 内容:
    • Tomatoesにしたがって、25分間作業、5分休憩の繰り返しを行う
    • Tomatoesは、途中で、15分の長め休憩を入れるが、それにも従う
    • 25分が終了した時点で、例外なく作業を止める
    • 休憩の間は、例外(トイレおよび電話などの同期割り込み)を除いて、目を閉じて椅子に深く座る
      • 目を休める効果を期待

結果

f:id:kmizushima:20191216195232p:plain
本日のプロジェクト

普段は、1日の仕事を終えた後に疲労感が残ることが多く、その時点での心拍数が100回/minを超えていること(安静時は70台なので、おそらく、緊張感によるもの)もしばしばでしたが、今日は80回/min程度でしたし、疲労感も少ないように感じます。また、「強制的に休憩をとらされる」ことの効果か、比較的長く集中が続いたように思えます。ただ、この辺は「気がする」という部分もあり、実際のとこどうなのか不明ですが。一方、「ここまでやらなきゃ欲」がセーブされるので、時間に追われる感覚が減るのは良い点かなと感じました。

ただ、この辺実践可能なのは、自宅でリモート作業可能だからであって、オフィスだと、そこまで実践できるかというと微妙な感じがします(30分~1時間のミーティングとかどう処理するのって気もしますし)。一方、「時間に作業を従属させる」という考え方は、集中の中断のために色々使えそうです。

Macro PEG再考:名前呼び出しと値呼び出し

Macro PEGは、私が2016年頃に、PEGに対する拡張として提案したものです。2016年のプログラミング研究会で発表したスライドが

kmizu.github.io

にあります。さて、当時考えたMacro PEGは、Macro PEGの引数付き規則を名前呼び出し(call by name)として評価するものでした。たとえば、

S = Twice("a")*;
Twice(A) = A A;

というMacro PEGがあったとき、Sから開始して、引数付き規則 Twice を引数 "a" で呼び出すと、Aは式"a"をそのまま束縛し、呼び出され側に展開される形になります。このため、これが受理する言語は、

L = {"aa", "aaaa", "aaaaaa", ...}

といったものになります。当時、値呼び出し(call by value)を検討したものの、あまり面白いことができなさそうだったので、名前呼び出しを採用したという経緯があったのですが、最近、改めて値呼び出しにした場合にどのようなことができるかを考えてみることにしました。

まず、PEGにおける「値」とは何かが曖昧なのですが、ここでは、ある式(parsing expression)に対してマッチした文字列だということにします。たとえば、

S = "a"*;

を入力 aaa に対して評価した場合、Sを評価した値はaaaになるものとします。このような考えに基づいて、Macro PEGの評価戦略として値呼び出しも選べるようにしました。

簡単な例で説明すると、

S = Twice("a")*;
Twice(A) = A A;

というMacro PEGは、値呼び出しの場合、先に、引数"a"と現在の入力文字列とのマッチが行われ、Aがその結果の文字列"a"を束縛して、Twiceを呼び出すものとします。ここで、さらに、

    1. 引数の評価の際に消費された文字列の続きから評価を続けるか
    1. 引数の評価の際に消費された文字列はいったん戻してから評価を続けるか

の二通りが考えられますが、まず、1.について考えてみます。

入力aaaに対して、1.の戦略で評価した場合、まず、引数の評価でaが消費され、残りの入力がaa、Aに"a"が束縛された状態で、評価が継続されます。その後、A Aとのマッチを続けるので、結果として、値はaaaになります。

一方、2.の戦略で評価した場合、まず、引数の評価の結果、A"a"が束縛された状態で、かつ、入力が消費されていない状態で、評価が継続されます。その後、A Aとのマッチを続けるので、結果として、値はaaになります。

さて、これだけだと値呼び出しにしてもあまり面白いことがないように思えますが、この「消費した文字列と引数を結びつけて、呼び出し先で使える」という性質を使って、XMLのタグの対応を書くことができます。

1.と2.のどちらでも実装できるのですが、1.の戦略だと、次のような感じになります。

S = F("<", [a-zA-Z_]+, ">"); 
F(LT, N, GT) = F("<", [a-zA-Z_]+, ">")* LT "/" N GT;

入力<a><b></b></a>に対して、マッチを試みてみます。

まず、XMLの開きタグをチェックするために、引数に "<", [a-zA-Z_]+, ">" を渡してマッチを行います(複数の引数がある場合は、前から順番に評価が行われるものとします)。引数の評価が終わった時点で、残りの文字列は<b></b></a>になります。

この状態で、LT=<N=aGT>としてFが呼び出されます。Fの中では再帰的にFを呼んでいます。一見左再帰のようにみえますが、引数の評価が失敗した場合、呼び出し自体も失敗するために左再帰になりません。F("<", [a-zA-Z_]+, ">")*の評価が終わった時点で、残りの入力文字列は</a>になっています。これは、LT "/" N GTとマッチするので全体が成功します。

このようにすることで、値呼び出しではXMLのタグの対応をチェック可能なことがわかりました。ちなみに、2.の戦略の場合でも、

S = "<" F([a-zA-Z_]+); 
F(N) = N ">" ("<" F([a-zA-Z_]+))* "</" N ">";

とすることで、同じことを表現することができます。これをもう少し一般化した表現で考えると、Macro PEGの値呼び出しは、実質的に後方参照とほぼ等しい機能をもっていると言えそうに思います。

ちなみに、値呼び出しと名前呼び出しを組み合わせることで、次のように、「未宣言の変数の出現を構文エラーにする」といった強力な記述が可能になります(のはずです)。*は名前呼び出し、!は値呼び出しを表すプレフィックスとします。

Id <- [a-zA-Z_]+
Int <- [0-9]+

Statements(*table) <- "val " ValCont(table, Id) | Expression(table) ";" Statements(table)

ValCont(*table, !id) <- id "=" Expression ";" Statements(table | id)?

Expression(*table) <- &table Id | Int

このMacro PEGは、val x=1;x;1;val x=1;val y=2;x;y;のような文字列を受理しますが、val x=1;z;のように、まだvalで宣言されていない変数が出現するような列を受理しません。

Macro PEGは名前呼び出しを使うことによって、引数に変数表のような役割を持たせることが可能で、一方で、値呼び出しによって、出現した名前を変数に入れておけるため、このような表現をすることが可能になりました。

名前呼び出しと値呼び出しを注釈によって使い分けられるMacro PEGの表現力はかなり強いのではないかと思えますが、どのくらいの言語が表現できるかはまだ未知ですが、色々面白い言語が表現できそうです。ちなみに、注釈はまだなものの、名前呼び出しと値呼び出し(2種類)は既に

GitHub - kmizu/macro_peg: Macro PEG: PEG with macro-like rules

実装してあり、ことができます(名前呼び出しの方は、パーザコンビネータ版しかテスト書いてませんが)。

Fortressのお話(あるいは、2007年1月のとあるWeb日記)

昨日、内輪でチャットしているときに、ふと、学部生~修士時代の自分のWeb日記(tDiary)を見てみたくなって、色々発掘してたら、面白いものが出てきてので、張り付けていこうと思います(ほとんどが駄文なので、なんか考古学的に(?)意味があるやつだけ載せる予定です)。で、今日載せるのはFortressの話。当時の日記の文面の原文をそのまま貼っただけなのであれですが、ポシャっちゃったFortressがどういう試みをしていたかという観点で興味深いかなと思います。なお、今の自分に比べて考察とか未熟なところがありますがご容赦を。あと、ツッコミは歓迎ですが、いかんせん当時の自分の考えなので、細かい理由は忘れてる可能性があります。

Fortressは、普通のプログラミング言語ですから、当然配列を使えるわけです。配列型の変数の宣言構文は次のような感じで、これはまあ全然すごくないというか普通です。ちなみに、ZZ32は32bit整数型です。Fotressの言語仕様をちゃんと読んだわけじゃないので、間違ってる可能性もありますが、とりあえず整数型であることは間違いないようです。

arr :ZZ32[3, 3] (* 3*3の2次元配列を宣言 *)

さて、配列をあらかじめ決まった要素で初期化するのにいちいち順番に代入していくのは面倒ですから、Fortressでも他言語のように配列のリテラル表記を使うことができます。

arr :ZZ32[3, 3] = [ 9 8 7
                    6 5 4
                    3 2 1 ]

改行によって配列の行の終わりを示せるというのは、まあ良いでしょう。面白いのは、配列の要素のセパレータとして空白を使うことができるところです。え、面白く無い?いやいや、前回のFortressに関するエントリに書いたのですが、Fotressではx yのように空白で区切って式を並べて書くことで、乗算を書くことができるのです。Fortressの配列リテラルの中では、通常の計算式を書くことができますから、単純に考えて文法を設計したら、

arr :ZZ32[3, 3] = [ (9 8 7)
                    (6 5 4)
                    (3 2 1) ]

のように解釈されてしまうと思うわけです。で、こんな文法になっているということは、「配列リテラルの中に出現する式」だけを特別扱いしているに違い無いと思い、Rats!で書かれたFortressの文法を読んでいたら、案の定そうなっていました。Literal.ratsファイルから該当すると思われる箇所を引用すると、

Expr Literal =
     openparen w closeparen
     { yyValue = new VoidLiteral(createSpan(yyStart,yyCount)); }
   / yyValue:NumericLiteral
   / yyValue:CharLiteral
   / yyValue:StringLiteral
   / opencurly w a1:Entry a2s:(w comma w Entry)* w closecurly
     { List<com.sun.fortress.interpreter.useful.Pair<Expr,Expr>> elements =
           new ArrayList<com.sun.fortress.interpreter.useful.Pair<Expr,Expr>>();
       elements.add(new com.sun.fortress.interpreter.useful.Pair<Expr,Expr>(a1.getKey(), a1.getValue()));
       for (Entry e : (List<Entry>)a2s.list()) {
           elements.add(new com.sun.fortress.interpreter.useful.Pair<Expr,Expr>(e.getKey(), e.getValue()));
       }
       yyValue = new MapExpr(createSpan(yyStart,yyCount), elements);
     }
   / opensquare w yyValue:RectElements w closesquare ;


/* RectElements ::= NoSpaceExpr MultiDimCons* */
MultiDim RectElements =
     a1:NoSpaceExpr a2s:MultiDimCons*
     { if (a2s == null || a2s.isEmpty())
           yyValue = new MultiDimElement(a1.getSpan(), a1);
       else
           yyValue = FortressUtil.multiDimCons(a1, a2s.list());
     };


/* MultiDimCons ::= RectSeparator NoSpaceExpr */
com.sun.fortress.interpreter.useful.Pair<Integer,Expr> MultiDimCons =
     a1:RectSeparator a2:NoSpaceExpr
     { yyValue = new com.sun.fortress.interpreter.useful.Pair<Integer,Expr>(a1,a2); };

赤字で強調したところがポイントで、おそらく配列リテラル中では「NoSpaceExpr」だけが出現できるということなんでしょう。で、この「NoSpaceExpr」が一体何なのかということですが、これは定義が複雑なのでとりあえず置いておきますが、ただ単に「スペースを含まない式」では無いようです。というのは、スペースで区切った式でも()で囲むことで配列リテラル中に書けるからです。

私なりのオブジェクト指向プログラミングの定義

きしださんの以下のツイート

を読んで、そういえば、私が思うオブジェクト指向の定義、についてツイッター以外ではあまり語ったことがなかったなと思い返し、ちょっと記事にしてみることにしました。まず、結論からいうと、私はオブジェクト指向プログラミングとは

  • サブタイピングを活用したプログラミング手法の総称

と考えています。ここで、クラス継承とかインタフェース継承とかダックタイピングとかではなく、単にサブタイピングであるのがポイントです。なお、型がない言語(RubyJavaScript、などなど)についても、実際にプログラミングをしているときは、たとえば「あるオブジェクトはメソッドm1, m2, ...を持っている」と仮定してコードを書いているはずだと思うので、それらは構造的部分型のようなものを使っているものとして考えます。

ここで、クラスもインタフェースも状態のカプセル化も一切でてこないことに違和感を抱かれる方もいると思うので説明します。まず、いわゆるオブジェクト指向プログラミング言語(言語の中でオブジェクトシステムを定義しているもの含む)には非常に様々なものがある、というのが重要です。簡単に分類しただけでも

といったものが思い浮かびます。3番目のものは、いわゆるオブジェクト指向プログラミングの主流派からは外れていますが、個人的にはそれらも含めて考えたいところです。これらの言語のすべてに共通する特徴を考えたときに残るのがサブタイピング(ないし極めて近い概念)を持っているということが挙げられます。というわけで、最大公約数を取ったときにはそうなるだろう、というのが私の結論です。

もちろん、主流のオブジェクト指向プログラミング言語C++Javaなど)ではクラスを活用すると思いますが、今やTypeScript(オブジェクトベースかつ、構造的部分型を使った型付け)も非常によく使われるようになってきていますし、それを無視してオブジェクト指向プログラミングを論じるのも無理筋だと思います。さらに、Scalaでよく行われるような、不変オブジェクトとメソッドを組み合わせたプログラミングスタイルも珍しいものではないわけで、可変状態ですら必須でないと言えます。

このように考えたときに、どうしても最後まで残らざるを得ないのがサブタイピングだと考えます。私が挙げた上記の言語すべてがそれらの概念(あるいは極めて近い概念)を持っています。とはいえ、普段、私がオブジェクト指向プログラミングとして意識するのは、クラスベースのオブジェクト指向プログラミング(特にできるだけ状態を不変にする)だったりするのですが、それはおいておきます。また、ここでは、私が思うオブジェクト指向プログラミングの定義について述べましたが、他の人がそれと違った定義をもっていてもいいと思います。

ただ、万人に共通するオブジェクト指向プログラミングのイメージというのは、おそらくもはや存在しないので、それぞれが違うイメージを持っていると認識した上で、定義をすりあわせた上で議論することが重要かなと思います。