ErgoDox EZ カスタマイズ情報のまとめ

先日買った ErgoDox EZ が届いたのでカスタマイズ中。GW に発注して一週間くらいで届いたので、かなり流通体制が整ってきている様子。

ergodox-ez.com

いぢりがいがあるのは良いのだけれど、正直なところ「素人お断り」感がすごいですね*1。というわけで、調べたことをまとめていこうかと。随時更新予定。

※記事中でリンクしているドキュメント類はかなり頻繁に場所が変わっているので、リンク切れの場合は頑張って探してください…。

ファームウェアのカスタマイズ

追記 (11/27): EZ 公式の GUI 設定ツールが登場しています。日本語キーの割り当てとかは部分的にしかサポートしてないっぽいですが…。

ErgoDox のキーマップを変更するには、本体ファームウェアの更新が必要。ファームウェアをカスタマイズするには、Massdrop の Ergodox Configurator を使うか qmk_firmware を自分でカスタマイズしてビルドする。Ergodox Configurator だと日本語キーやマクロなどが使えないので、その場合はファームウェアのビルドが必須になる。

ビルド手順はプラットフォームごとに整備されているので、詳細はドキュメントを参照してほしい。手順自体は、依存ライブラリをインストールしたら keyboards/ergodox/ に行って make するだけなので、そこまで難しくないはず…。

キーマップの定数の意味

keymap.c に出てくる KC_XXXX という定数は、様々なキーコードに対応している。キーをカスタマイズする際は、これを所望のキーコードに書き換えてやる。

qiita.com

公式のドキュメントは以下にある:

ファームウェアのインストール

Teensy を使ったファームウェアの書き換え手順。リセットスイッチを押すのに細長いものが必要なので、手元にゼムクリップを用意しておくとよい。

qiita.com

Hyper キーと Meh キー

デフォルトレイアウトにある Hyper キーMeh キーってなんやねん、と思って調べてみた。これは、通常の方法では入力できない修飾キー (Alt + Ctrl + Shift + Cmd) を組み合わせることで、起動中のアプリケーションと被らない、どのタイミングでも使用できるホットキーを設定できる、というものなんだそうだ(追加でキーボードショートカットを設定するユーティリティアプリが必要)。例えば、エディタを操作してる最中に Hyper + S で Slack アプリを呼び出す、みたいなことができる。Meh キーは Cmd キーを削った Windows 向けのバージョン(Windows に Cmd キーはないから)。

元々、OS X で疑似的に Hyper キーを実現するというテクニックがあって、それをキーボード自体に組み込んだものらしい:

JIS(日本語)レイアウト固有のキーについて

ErgoDox の初期設定は US レイアウトを前提にしているので、JIS レイアウトで使うには、例えば「半角/全角」キーは自分で割り当てる必要がある。

JIS レイアウトでの定数とキーの対応関係は、下記の記事にまとまっている:

qiita.com

仕様とかの解説は以下:

OS X で日本語関連のキーが動作しない問題があるようだ(正確には、OS X で動くように修正すると逆に WindowsUbuntu で動かなくなる)。解決してメインラインにマージされたっぽい(255signed-1 だったぜとか書いてあってマジかってなった)。

github.com

ファームウェアの CI 環境

自宅以外の環境でも調整できるようにしたかったので、id:ymotongpoo さんのブログ記事を参考にして CI 環境を構築した。qmk_firmware のビルド手順は頻繁に変わっていていちいち対応するのも大変なので、普通はここまでする必要はないと思う。

wercker status

QMK の公式レポジトリに追随したかったので、直接 fork して develop ブランチを切った。

github.com

僕が使っているファームウェアは以下からダウンロードできる(キーマップ):

Download firmware

f:id:okapies:20161225160928p:plain:w800

やっていることは、依存ライブラリをインストールした Docker コンテナで make しているだけ。最新バージョンに対応した wercker.ymlGitHub に置いてあるので、もし自分で CI 環境を組む際はどうぞ(wercker 側で $TARGET_KEYMAP 環境変数の設定が必要)。Docker Image を自分で用意する場合はこの Dockerfile を使ってください。

キーキャップ

ErgoDox EZ は Cherry MX キースイッチなので、Cherry 用のキーキャップを流用できる。ただし、ErgoDox は親指キーや周辺キーのサイズが通常のキーボードと異なるので、この部位に対応する特殊なキーキャップの入手が課題になる。必然的に特注品になってしまうので高価なことが多い。

ErgoDox 用キーキャップ

前述の通り、ErgoDox のキーキャップをカスタマイズする場合は、特殊なサイズのキーキャップを揃える必要がある。必要数は以下の通り(参考1参考2):

  • 1x: 60 個(普通のキーと同じサイズ)
  • 1.5x: 12 個(Tab キー程度のサイズ。両脇に配置)
  • 2x: 4 個(シフトキー程度のサイズ。親指部分に配置)

また、段差付き(=各段で形状が異なる)の sculptured のキーキャップの場合は、各段 (row) ごとに個数を合わせる必要がある。以下は一例(参考):

  • 16 - Row 1 - 1u
  • 14 - Row 2 - 1u
  • 10 - Row 3 - 1u
  • 20 - Row 4 - 1u
  • 2 - Row 1 - 1.5u
  • 2 - Row 2 - 1.5u
  • 6 - Row 3 - 1.5u
  • 2 - Row 4 - 1.5u
  • 4 - Row 2 - 2u

段数の数え方はキーキャップのメーカーによって異なるので注意してほしい。

プロファイル

市場に出回っているキーキャップには様々な形状があり、形状ごとに「○○プロファイル (profile)」という名前で呼ばれている。プロファイルの詳細については、Reddit の WikiGeekhack のスレッドなどが詳しい。代表的なものには以下がある:

  • Cherry profile
  • OEM profile
  • SP (Signature Plastics) 製
    • DSA profile
    • DCS profile
    • SA profile

ErgoDox EZ 公式で買えるのは、文字なしで段ごとに形状の異なる (sculptured) の DCS プロファイルと、文字入りで上面が真っ平な代わりにキャップ同士の入れ替えが可能な DSA プロファイルの二つ。

11月頃?からキーキャップの単品売りもしてくれるようになったが、かなりいいお値段 ($90) なので本体とセットで買った方が良い(これでも相場的には安い方だったり…)。

JIS 配列の文字刻印が入っているキャップは、現状では下記の FILCO が販売している製品しか入手できない。

当然、ErgoDox 用の 1.5x や 2x のキャップは含まれていないので EZ 付属のキャップと組み合わせる必要がある。しかし、これは OEM プロファイルというかなり背丈が高いプロファイルで DSA プロファイルとは組み合わせにくい(DCS もおそらく同様)。そこを我慢して使うならアリか。

材質

ABS 樹脂製と PBT 樹脂製の二種類がある。基本的には PBT の方が高級で、耐久性が高く摩耗によるテカリが出にくいが、加工しにくいという難点がある。そのため、特にデザイン性の高いキーキャップには ABS 製が多く、PBT 製は刻印のない無地のキーキャップに多い。

また、キートップに印刷されている刻印が摩耗で消えてしまう問題があるが、"double-shot" と呼ばれる二色成形のキーキャップなら回避できる。

入手方法

国内ではほぼ入手できないので、海外からの輸入が基本になる。

キーキャップメーカーの詳細は下記の記事が詳しい:

tocet.hatenablog.com

キーキャップをバラ売りで欲しい場合は、Pimpmykeyboard.comWASD Keyboards などのオンラインショップから購入できる*2。色や形をカスタマイズしたい場合は利用することになるだろう。その他の取り扱い店の情報はこちら

自作キーボード界隈では、Massdrop に代表される共同購入 (group buy) サイトで不定期に募集される特注品のキーキャップを購入することも一般的に行われている。普通は通常のキーボード用ばかりだが、稀にオプションで ErgoDox 用セットが提供される場合がある。いずれの募集 (drop) も一週間程度で募集が締め切りになるので、定期的にチェックしておくとよい。2chキーキャップ総合スレでは Massdrop 以外の共同購入の情報も共有されている(物によっては品質が怪しい場合もあるようなので注意)。

WASD Keyboards では、キーキャップに好きな文字をプリントできるオプションを提供している(1個あたり7ドル)。お金があるなら JIS 配列を刻印したものを作ってもらう手も…?

ケーブル

この項については ErgoDox users meet up で発表した。

speakerdeck.com

仕様

ErgoDox は、マシン本体との接続に使う Mini USB の他に、セパレートしたキーボード同士を接続するための 直径 3.5mm の TRRS ケーブルが必要。これは、国内では「4極ステレオミニプラグ」と呼ばれてオーディオ用で流通しているものと同じもの。

2極や3極との見分け方は、コネクタ部分が3本の線4つに分かれているかを調べる。

市販品

ErgoDox EZ に付属してくる TRRS ケーブルは長めで無骨な感じのものであり、できればもっといい感じのものに買い替えたい。が、4極のオス/オスでケーブル長が短めのもの、という製品は数が限られる。

現状では、ダイヤテックが販売している Matias Ergo Pro 用のケーブルを流用するのがおすすめ。巻き取り式なので長さの調節が容易なのが良い。

あと、こういうのもあるが、買ってみたところかなり短いので広げて配置したい人には厳しそう。

オーダーメイド

ErgoDox 向きの素敵な TRRS ケーブルが不足しているのは海の向こうでも事情は同じらしく、自作しちゃう人もいるようだ。

そんな需要に応えてなのか、ケーブルをオーダーメイドで作ってくれるサービスがあり、色とか長さとかを自由に選べる。日本への配送もやってくれるっぽいが、送料込みで四~五千円くらいするのが難点。

CPU 切替器(KVM スイッチ)

今までサンワサプライ製の CPU 切替器を使っていたんだけど、どうやら ErgoDox とキーボードエミュレーション機能の相性が悪いらしく、差しても Windows で認識されない。*3

以下、手に入る範囲でいくつか試したので紹介する(6/10 現在)。現在は ATEN の製品を使っていて、マウスキーが使えない以外は切り替え時のラグもなく満足している。

メーカー 製品名 Linux (Ubuntu) Windows マウスキー 備考
サンワサプライ SW-KVM2LU ◯(即時) × ディスコン。後継機は SW-KVM2LUN
バッファロー BSKMRA201 ◯(ラグ小) △(ラグ大) -
RATOC REX-430UDA ◯(ラグ小) △(ラグ大) -
ATEN CS692 ◯(即時) ◯(即時) × -

ATEN は国内メーカーに OEM 供給しており、中身は ATEN 製というものがけっこうある。ATEN 自身のブランドで販売されているものは、ファームウェアの更新に対応している点がメリット。サンワサプライ製品も ATEN OEM が多いので、最近の製品でファームウェアが最新になっているなら使えるかもしれない。

*1:プログラマでも、現状では、普段からソースコードを読んだり仕様を調べたりする習慣がない人には厳しい感じがする。デフォルトの設定でそのまま使っても良いんだろうけど、それなら Kinesis とか買えばいいわけだし。

*2:Pimpmykeyboard は、EZ にキーキャップを供給している Signature Plastics が運営する EC サイト。

*3:Ubuntu 12.04 では動いたので、Windows の仕様と関係がありそう。

ScalaMatsuri 2016 でスタッフ&発表やりました

だいぶ出遅れましたが、ScalaMatsuri 2016 の参加報告です。

scalamatsuri.org

発表者として

今回、CFP に応募して当選した「なぜリアクティブは重要か (Why Reactive Matters)」というタイトルで発表をさせていただきました。投票してくださった皆様、聞きにきてくださった皆様、ありがとうございます。

発表に使ったスライドは以下になります(発表は英語スライドでしたが日本語化しました):

www.slideshare.net

あと、Togetter でまとめて頂いたツイートはこちら

今回はプログラミングの話題に重点を置きつつ、〈リアクティブ・コンポーネント+データフロー〉という構図が、非同期プログラムのコードから分散システムのアーキテクチャまで、様々なスケールで出現する様子をご紹介しました。

最後でチラリと触れた「イミュータブル・インフラストラクチャ (Immutable Infrastructure) みたいなキーワードが流行ったわりには、DevOps のツールチェインって命令型ばっかで関数型の知見が全然入ってないよね」という話は、そのうちどこかで改めて扱いたいと思っています。

あと、タイトルの元ネタである「なぜ関数プログラミングは重要か」自体についてはあまりちゃんと紹介できませんでしたが、先日、解説記事を書きましたのでご興味があればぜひ。

81 枚分の内容を 40 分の時間内に収めて話すという所はクリアしたものの、「内容が頭に入ってこなかった」というご意見も頂いており、そこは反省点ですね。精進します。

参加者として

当日はスタッフとしてバタバタしてたので頻繁に中座してましたが、聞きたかった発表については翻訳チーム内でローテーションを調整してもらったのでガッツリ聞けました。ありがとうございます。

一日目は、とにかく Jonas Bonér さんの発表が圧巻でした。スライドだけ見ていると「???」という所が多いのですが、実際に聞くと本当に分かりやすい。あと、スライドは字幕付け作業の一環で事前に目を通していたのですが、まさか冒頭で「○ッキー」のテーマをブッ込んでくるとは思わなかった(笑。

レジリエンスというキーワードについては、今回、翻訳チーム内でもどう訳すかについて散々議論したのですが、日本語にピッタリと対応する言葉がない、なかなか奥深い概念です。リアクティブ・マニフェストの翻訳では〈耐障害性〉としたのですが、そのまま〈レジリエンス〉と訳すより仕方がないという結論になったので、マニフェストの方も変更するかもしれません。そのうち講演動画もアップする予定なので、その際に私も解説記事とか書きたいですね。

二日目は、朝会に始まり Typesafe メンバーのパネルと Scala 社内教育のパネルと、一日に三回も司会に登壇するという自分でも謎の大車輪をやっていました。貴重な経験ができたと思います(笑。

来日中の Typesafe メンバー全員に参加していただいたパネルディスカッションについては、まぁぶっちゃけると私がどうしても話を聞きたかったので、麻植さんに出演交渉の仲介をお願いしたり、TIS の前出さんと根来さんにも加わって頂いて作戦会議したりして、なんとか形になりました。皆様、本当にありがとうございます。

内容としては、Typesafe 社の技術開発やビジネスの方針について、今までドキュメントやプレスリリースから憶測していた部分のニュアンスがかなり明確になったので、個人的には大満足でした。今までウェブ上で明言されてなかった話とかも飛び出したし…。

社内教育パネルについては、そもそもの「何を、どういう順で教えるべきか」という点が人によって意見に隔たりがあることが確認できたものの、できればもう少し深堀りできると良かったですね。司会として、事前にインタビューとかしておくべきでした。

スタッフとして

今回、準備委員会では「翻訳チーム」のリーダーを任せていただきました。仕事内容はこんな感じですか:

  • 同時通訳者の派遣の手配と、当日の対応
  • メールやリリースの文面の翻訳・レビュー
  • CFP の英訳・日本語訳とウェブサイト掲載
  • 発表スライドの CoC compliance レビュー、英文レビュー、字幕作成、etc..
  • 海外参加者の渡航に関するフォロー

今回、同時通訳はケイワイトレードさんにお願いしました。PyCon の方から良い評判を伺っていたことが決め手になって依頼したのですが、当日ご来場頂いた方はお分かりの通り、非常に質の高い通訳を提供して頂けました。

これは、通訳者の皆さんがプログラミングについての知識をしっかりとお持ちであったことが大きいです。また、発表者に対するインタビューなどの事前準備もしっかりしていました(自分がインタビューを受けた際も、「アクターは、オブジェクト指向で言うところのオブジェクトと同じものですか?」という的確な質問を受けて感心しました)。今回の ScalaMatsuri は、関数型プログラミングの専門用語が飛び交うたいへん難しい依頼だったと思いますが、期待以上の仕事をして頂けたと思います。

翻訳の実務については、今年も横田さん (@eed3si9n_ja) に大きく依存してしまい申し訳ありませんでした…。ただ、今年は皆様のご協力のおかげで、去年よりもワークロードが分散化できたかなと思っています。竹井さん (@taketon_)、木村さん (@kimutansk)、田中豪さん (@tan_go238)、田中翔さん (@tshowis)、岡田さん (@ocadaruma)、大村さん (@everpeace)、青山さん (@aoiroaoino)、河内さん (@kawachi)、その他ご協力頂いたスタッフの皆さん、ありがとうございました(順不同)。

行動規範 (CoC) について

昨年から始めた行動規範の文面の見直しや、マナー動画のアイデア出し、各種資料の compliance レビューに参加しました。

www.youtube.com

行動規範のあり方についてはスタッフ内にも色々な考え方がありますが、個人的には規範的な面より啓発的な面を重視すべきという考え方で意見をしていました。要は、ルールを定めるのは「開かれたカンファレンス」の実現が目的であって、違反者の断罪が目的ではないということです。

Scala Matsuri は、様々な地域やコミュニティから集う技術者に対して開かれたカンファレンスを目指しています。特に、性別や人種など、多様な背景を持つ人々が互いに敬意を払って楽しい時間を過ごせるよう、当カンファレンスでは、発表者や参加者、スポンサーの皆様に以下の行動規範を守っていただくようにお願いしています。

また、こうした取り組みは我々だけがやっても意味がないので、他のカンファレンスにも輪を広げていきたいと思っています。今回の行動規範の文面やマナー動画は、皆さんのカンファレンスでどんどんパクって頂くことを前提に作っています。ご自由にお使いください。

一方で、行動規範を真面目に運用しようとすると、資料のレビューやクレーム対応等に少なからずコストをかける必要があるのも確かです。技術カンファレンスの多くがボランティアで運営されている以上、いきなり完全なサポートは難しい場合も多いと思います。

私は、段階的な導入で構わないと思います。行動規範は、先ほども書いたように、「正義の実現のため」というよりも「参加者の間口を広げるため」にあると考えるからです。また参加者の方も、自分が参加するカンファレンスに行動規範がない場合に、そのことを責めるのではなく、自分から手を挙げて仕組みの整備に協力するくらいの心構えを持って頂けると良いのではないかと思います。

まとめ

2013 年の Scala Conference in Japan 以来、3回ほどスタッフとして関わってきましたが、国内外の濃い面子がここまで一堂に会する機会を実現できたのは始めてだと思います。個人的には、他の技術カンファレンスを通して考えても例がありません。例えば、吉田さん(Scalaz コミッター)と Bill Venners さん(Scalatest 作者)の対話が実現した件は、その最良の成果の一つだと思います。

また、同時通訳については導入して本当に良かったと思います。私も含めて、数年前よりも英語スキルが上がってきている面子が増えているものの、双方向に議論をするとなると、まだブ厚い壁が存在します。その垣根が取り払われることで、日本語話者と英語話者が同時に登壇してパネルディスカッションをやる、といったことも実現できるようになりました。「Scala の国際カンファレンスをやる」という目標に対して、大きく近づいたと思います。

参加者の皆さん、発表者の皆さん、そしてスタッフの皆さんの貢献に感謝します。ありがとうございました。

ReactiveSocket について

この記事は、Java Advent Calendar 2015 の 22 日目です。前日は、n_slender さんの「PlayFramework 2.4 Java Ebeanでのアプリ開発」でした。

今日の記事では、この半年くらいで仕様と実装が出てきている ReactiveSocket というプロトコル仕様についてお話したいと思います。

なぜ Java Advent Calendar でプロトコルの話を? と訝しがっている方も多いと思いますが、基本的には以下の二つの理由です。

  1. JEP 266 として JDK 9 に追加される予定の Reactive Streams と密接に関わっている
  2. Java 製のサーバサイド向けライブラリを多数 OSS 化している Netflix が中心になって仕様策定を行っており、参照実装も JVM 向けが中心

予定ではプロトコルレベルの話にも踏み込んで解説したいと思っていたのですが、プライベートが色々と立て込んでいるため、概要レベルのご紹介になることをお許しください。

ReactiveSocket って何?

ReactiveSocket is an application protocol providing Reactive Streams semantics over an asynchronous, binary boundary.

(ReactiveSocket とは、非同期バイナリ境界をまたいで Reactive Streams のセマンティクスを提供するアプリケーションプロトコルである。)

ざっくり言うと Reactive Streams の考え方をアプリケーションプロトコルのレイヤで実現するための仕様。

そもそもの Reactive Streams とは何か、については以前に書いた記事でも解説しているのでご参照ください。

okapies.hateblo.jp

要点としては、メッセージ駆動のコンポーネント間でメッセージをやり取りするシステムを組んだ際のフロー制御の方法を定めている。

具体的には、送信側が受信側の処理能力を超える量のメッセージを送信してバッファを溢れさせることのないように、受信側から送信側に対して「次は◯個送っていいよ」というフィードバック (back-pressure) を通知することで、過負荷の際に処理能力を超えるメッセージを受信してシステムがクラッシュする事態を回避することを狙っている。詳細な動作については、以下のスライドの図も参考にしてほしい:

特徴

ReactiveSocket の特徴は以下の通り:

メッセージ駆動

(HTTP2 と同様の)非同期なメッセージ駆動であり、全ての通信は、単一のコネクション上に多重化されたメッセージストリームを介して行う。また、これによってレスポンス待ちでブロックすることがなくなる。

相互作用モデル

ReactiveSocket は複数の相互作用 (interaction) モデルをサポートしている。ユースケースごとに適切なモデルを選んで使用することで、性能やユーザ体験に与える影響を向上できる。

また、後述するようにトランスポートに何を使うか (TCP, WebSocket, Aeron, ...) に依存しないので、これらを使ってアプリケーションを実装すれば、性能特性に合わせてトランスポートを入れ替えることもできる。

  • Fire-and-Forget(撃ちっぱなし)
    • レスポンスが必要ない場合は、これを使うのが一番効率的
    • Future<Void> completionSignalOfSend = socketClient.fireAndForget(message);
  • Request/Response(単一レスポンス)
    • 普通のリクエストレスポンス。「レスポンス1個のストリーム」を最適化したものと考えることができる
    • Future<Payload> response = socketClient.requestResponse(requestPayload);
  • Request/Stream(有限個の複数レスポンス)
    • 「コレクション」や「リスト」に相当
    • Publisher<Payload> response = socketClient.requestStream(requestPayload);
  • Topic Subscription(無限個の複数レスポンス)
    • 「プッシュ通知」や「イベントストリーム」に相当
    • Publisher<Payload> response = socketClient.requestSubscription(topicSubscription);
  • Channel(双方向ストリーム)
    • クライアント側から途中でリクエストの条件を変更したりするような場合に用いる
    • Publisher<Payload> output = socketClient.requestChannel(Publisher<Payload> input);

フロー制御

二つのフロー制御方式をサポートしている。どちらも、トランスポートレイヤではなくアプリケーションレベルの流量制御に焦点を置いている。

一つは、Reactive Streams が仕様化しているような request(n) の非同期プル。こちらは、リクエスト発行側 (requester) から応答側 (responder) へのキャパシティの通知に使う。

もう一つは ReactiveSocket 独自のリース (leasing) という仕組みで、応答側から発行側へのキャパシティ通知に用いられる。リースは、「規定時間 (TTL) までに◯個まで送ってよし」という形式でリクエストを発行する。これによって、データセンター内のサーバ間通信のようなユースケースで、アプリケーションレベルの負荷分散(クライアント側で、各サーバから通知されたリースの情報を使ってリクエストを分散する)がやりやすくなる。

多言語 (polyglot) サポート

相互作用モデルとフロー制御を言語非依存なプロトコルとして定義しているので、言語を跨いだインタラクションに利用できる(Reactive Streams は JVM 上で動作するミドルウェア同士でしか利用できない)。

様々なトランスポートレイヤをサポート

ReactiveSocket 自体は OSI Layer 5/6 相当のアプリケーションプロトコルであり、TCP 以外にも WebSocket や Aeron (*)、Quic といった様々なトランスポートプロトコルの上に実装できる。

また、ReactiveSocket が定義するアプリケーションレイヤはトランスポートの差異を隠蔽するので、ユースケースに合わせて最適なトランスポートを選ぶことができる。

*: Reactive Manifesto の執筆者の一人である Martin Thompson の会社 Real Logic が開発しているトランスポートプロトコル。元 LMAX の CTO で Disruptor を開発していた御仁、といえば分かる方もいるのでは。

性能

コネクションを使い回すので、コネクションを何度も張り直すような余計な処理を回避できる。また、バイナリプロトコルなので CPU 負荷を削減できる。さらに、フロー制御が組み込まれているので、相手先システムがスローダウンしている時にリトライ地獄を仕掛けてさらに負荷をかけるようなことがない。

同様の課題を解決する仕組みとして、Netflix が自身のマイクロサービス同士のフロー制御に使っている Hystrix があるが、オーバヘッドや複雑さが増すという問題点があった。

なんで HTTP/2 を使わないの?

大雑把に言うと、HTTP/2 は一義的にウェブサイトからドキュメントを取得するブラウザのためのプロトコルで、ReactiveSocket が想定するユースケースに合わないから。

  • リクエスト/レスポンスのみで、それ以外の相互作用モデルをうまくサポートできない
  • アプリケーションレベルのフロー制御の仕組みがない
  • REST は非常に普及しているが、アプリケーションのセマンティクスを定義するのに使うのは非効率で不適切である

対応実装と今後について

コアライブラリとして reactivesocket-java が公開されている。これ自体はプロトコル実装を Reactive Streams API でアクセスできるようにしたもので、実際には以下のような具体的なトランスポートプロトコルの実装でラップして使う:

また、ブラウザや Node.js から使える JavaScript 版の実装も作られている:

今後…については最近あまり追いかけられてないので分からないです。すいません。とりあえず、Netflix 内部のフロー制御を Hystrix から置き換えていきたいのだとは思われる。具体的な進捗を知ってる人がいたら教えて下さい。あとは、Reactive Streams に参加してる他のベンダー(Typesafe とか)が乗っかるのかどうか(いちおう呼びかけはなされていて、Typesafe の人も関心はあるみたい)。

既に見たように、Reactive Streams 自体は JVM に閉じた仕組みだったところを、アプリケーションレイヤープロトコルとして仕様化することで多言語で活用できる可能性が出てきたわけで、個人的には注目しています。

「なぜ関数プログラミングは重要か」を要約してみた(その1)

関数型プログラミング (functional programming) の利点を説く際によく持ち出されるのが、QuickCheck の開発者の一人である John Hughes が 1984 年に著した論文 "Why Functional Programming Matters" だ。「なぜ関数プログラミングは重要か」という題名で日本語訳もされているので、読んだことがある人も多いと思う。

要旨としては、冒頭の1章および2章で述べられている「関数型プログラミングが優れているのは、高階関数遅延評価という、モジュール同士を貼り合わせる強力な『糊』を持っているからだ」という話がほぼ全てで、以降はそれを具体例に基づいて説明する構成になっている。ただ、その具体例として「数値計算アルゴリズム」やら「ゲーム用人工知能アルゴリズム」やらの話が延々と続くし、しかもコード例が Haskell の先祖にあたる Miranda という言語で書かれているのでなかなか取っ付きづらい。

今回、来年の1月に ScalaMatsuri で「なぜリアクティブは重要か」というお題で話をさせて頂けることになったこともあって、少し頑張って、元ネタであるこの論文を通読したので要約を公開したいと思う。なお、コード例は Scala で書いた(Scala 版のコードはこの記事を参考にしている)。

1. イントロダクション

  • 本論文の目的は、関数型プログラミングの重要性を示すと共に、その利点を明確にしてフル活用できるようにすること
  • 関数型プログラミングでは、プログラム全体を関数だけで構成する
    • メインプログラム自身が関数であり、プログラムへの入力を引数として受け取り、結果をプログラムの出力として供給する
    • メイン関数はさらに多くの関数を使って定義されるので、プログラムの最下層に至るまで関数は言語のプリミティブとなっている
  • 関数型プログラミングの「利点」:
    • 関数型プログラムには副作用(≒代入文)がないのでバグが減らせる
    • 参照透明なので実行順序を気にしなくてよく、式をどの時点で評価してもよいのでプログラムをより数学的に扱える
  • それはそうなんだけど…
    • 「〜ではない」についてばかり語っている(代入文がない、副作用がない、制御フローがない
    • 「〜である」について語らないと、物質的な利益に興味がある人にはピンと来ないだろう
  • 関数型プログラミングの力を語るだけでなく、それが目指す理想を示さねばならない

2. 構造化プログラミングとの類似

  • 関数型プログラミングと構造化プログラミングを比較してみる
  • 構造化プログラミングとは、「goto 文を含まず」「ブロックが複数の入口や出口を持たない」
    • さきほどの関数型プログラミングの「利点」と同様に、否定形の説明になっている
    • 「本質的な goto」のような実りのない議論の温床になった
  • 構造化プログラミングの核心はモジュール化であり、大きな生産性向上をもたらす
    1. 小さなモジュールは素早く簡潔にコーディングできる
    2. 汎用モジュールの再利用によって、プログラムをより速く開発できる
    3. モジュールは独立してテストできるので、デバッグが容易になる
  • goto は小規模プログラミングでしか役立たないが、モジュール化設計は大規模プログラミングにおいても役立つ
  • プログラミング言語が問題をモジュール化する能力を高めるには、モジュール同士を貼り合わせるが重要
    • 問題を部分問題に分割し、部分問題を解き、その解を合成する。つまり、問題を分割する方法は、解を合成する方法に依存する
    • 例: 椅子を部品(座部、脚、背もたれなど)に分けて作れるのは、ジョイントや木工接着剤があるから。さもなければ、一つの木の塊から椅子を掘り出すしかない

3. 関数の貼り合せ

この章では、二種類のの一つ目である高階関数について紹介している。sum のような単純な関数を、高階関数とその引数の組み合わせとしてモジュール化することで、reduce のような汎用的な関数を導出する。

論文では、二つのデータ型(リストとツリー)に対して適用できる高階関数について述べている。まず、リスト操作関数の汎用化を進めて、最終的に reduce 関数と map 関数を導出する。次に、木(ツリー)構造に対する操作についても同様に redtreemaptree を導出する。

章の最後では、「汎用の高階関数と特有の特殊関数の組み合わせとして部品化することで、たくさんの操作を容易にプログラムできる」「新たなデータ型を定義したときは、それを処理する高階関数を書くべきだ」と結んでいる。

リスト編

リスト処理の問題を例に説明する。リストのデータ構造を(Scala で)書くとこうなる:

sealed trait ListOf[+X]
case object ListNil extends ListOf[Nothing]
case class Cons[X](head: X, rest: ListOf[X]) extends ListOf[X]

以上のデータ構造を使って具体的なリストを表すとこうなる:

[]        は ListNil
[1]       は Cons(1, ListNil)
[1, 2, 3] は Cons(1, Cons(2, Cons(3, ListNil)))

次に、リストの要素を足し上げる関数 sum を定義してみる:

def sum: ListOf[Int] => Int = _ match {
  case ListNil         => 0
  case Cons(num, list) => num + sum(list)
}

この定義を調べると、sum に固有なのは初期値 0 と演算 + だけなのが分かる*1。つまり、sum

  • 一般的な再帰パターン(reduce と呼ばれる)
  • sum 固有の部分(0+

の二つにモジュール化して、後で貼り合わせることでも作ることができる:

def add(x: Int, y: Int) = x + y
def reduce[A, B](f: (A, B) => B, x: B)(list: ListOf[A]): B = list match {
  case ListNil    => x
  case Cons(a, l) => f(a, reduce(f, x)(l))
}

def sum: ListOf[Int] => Int = reduce(add, 0)

(注: Scala でこの書き方をするとスタックが溢れるけど、回避方法は色々なところで紹介されているので略。あと、カリー化の話も略。)

reduce は(初期値と演算を入れ替えるだけで)様々な用途に再利用できる:

// リストの全要素の積
def product: ListOf[Int]     => Int     = reduce(multiply, 1    )
// リストの要素のいずれかが true か調べる
def anytrue: ListOf[Boolean] => Boolean = reduce(or,       false)
// リストの全要素が true か調べる
def alltrue: ListOf[Boolean] => Boolean = reduce(and,      true )

ところで reduce は、リストの Cons の部分を f で、ListNil の部分を a で置き換えたものと看做せる:

                     l =     Cons(1,     Cons(2,     Cons(3, ListNil)))
     reduce(add)(0)(l) =      add(1,      add(2,      add(3,       0)))
reduce(multiply)(1)(l) = multiply(1, multiply(2, multiply(3,       1)))

つまり、reduce(Cons, ListNil) は(ConsCons に、ListNilListNil に置き換えているだけなので)リストからリストを複写する関数とみなせるし、リスト ab に対して reduce(Cons, b)(a) は二つのリストを連結する関数となる:

def copy[A](l: ListOf[A])                 = reduce(Cons[A], ListNil)(l)
def append[A](a: ListOf[A], b: ListOf[A]) = reduce(Cons[A], b      )(a)

次に、リストの全ての要素を2倍したリストを返す関数 doubleAll は、doubleAndCons 関数を使って以下のように定義できる:

def doubleAndCons(num: Int, list: ListOf[Int]) = Cons(2 * num, list)

def doubleAll(l: ListOf[A]) = reduce(doubleAndCons, ListNil)(l)

doubleAndCons 関数は、以下のように double 関数と fAndCons 関数の組み合わせに置き換えられる:

def double(n: Int) = 2 * n
def fAndCons[A, B](f: A => B)(el: A, list: ListOf[B]) = Cons(f(el), list) // 2 * num => f(el)

def doubleAndCons: (Int, ListOf[Int]) => ListOf[Int] = fAndCons(double)

ところで fAndCons 関数は fCons を合成した関数 (Cons . f) として定義することもできる:

def fAndCons[A, B](f: A => B): (A, ListOf[B]) => ListOf[B] = (Cons[B] _).compose(f)

(注: 上記を実行するには、あらかじめ以下の暗黙変換を定義して import RichFunction2._ しておく必要がある。)

implicit class RichFunction2[T1, T2, R](f: Function2[T1, T2, R]) {
  def compose[A](g: (A) => T1): (A, T2) => R = (x: A, y: T2) => f(g(x), y)
}

したがって、doubleAll 関数は double, Cons, reduce の組み合わせで定義できる:

def doubleAll: ListOf[Int] => ListOf[Int] = reduce((Cons[Int] _).compose(double), ListNil)

ここで、double 関数をパラメータ化すると、リストの全要素に f を適用する map 関数を導出できる:

def map[A, B](f: A => B): ListOf[A] => ListOf[B] = reduce((Cons[B] _).compose(f), ListNil)

def doubleAll: ListOf[Int] => ListOf[Int] = map(double)

map は汎用的に使える有用な関数で、例えば行列(=リストのリスト)の要素を足し上げる関数を作りたくなっても、以下のように簡潔に書ける:

def sumMatrix: ListOf[ListOf[Int]] => Int = sum.compose(map(sum))

ツリー編

ラベル付きの順序付きツリーについて考えてみる。(Scala で)データ構造を書くとこんな感じ:

sealed abstract class TreeOf[A] {
  def label: A
  def subtrees: ListOf[TreeOf[A]]
}
case class Node[A](label: A, subtrees: ListOf[TreeOf[A]]) extends TreeOf[A]

例えば、以下のようなツリーを:

    1 o
     / \
    /   \
   /     \
2 o       o 3
          |
          |
          |
          o 4

上記で定義したデータ構造で表すとこうなる:

Node(1,
     Cons(Node(2, ListNil),
          Cons(Node(3,
               Cons(Node(4,
                    ListNil),
               ListNil)),
          ListNil)))

リストの時と同様に、reduce と同じ役割を果たす redtree 関数を考えてみる。reduce は「Cons を置き換える何か」と「ListNil を置き換える何か」の二つを引数に取る関数だった。同じ方針で考えてみると、redtreeNodeConsListNil を置き換えた三つの何かを引数に取る関数になるはずだ。

def redtree[A, B, X](f: (X, A) => B, g: (B, A) => A, a: A)(tree: TreeOf[X]): B = {
  def redtreeImpl[A, B, X]
      (f: (X, A) => B, g: (B, A) => A, a: A)(subtrees: ListOf[TreeOf[X]]): A =
    subtrees match {
      case Cons(subtree, rest) => g(redtree(f, g, a)(subtree), redtreeImpl(f, g, a)(rest))
      case ListNil => a
    }

  f(tree.label, redtreeImpl(f, g, a)(tree.subtrees))
}

reduce と同様に、redtree を他の関数と組み合わせて様々な関数が定義できる:

// ツリー全体の label を足し合わせる関数
def sumtree: TreeOf[Int] => Int       = redtree(add,     add,       0      )
// ツリー全体の label のリストを作る関数
def labels[A]: TreeOf[A] => ListOf[A] = redtree(Cons[A], append[A], ListNil)

最後に、ツリー用の map 関数である maptreeredtree を使って定義しておく(5章でゲーム用人工知能を実装する際に使う):

def maptree[A, B](f: A => B): TreeOf[A] => TreeOf[B] =
  redtree((Node[B] _).compose(f), Cons[TreeOf[B]], ListNil)

インターミッション

ちょっと力尽きたので、今回はここまで。続きはやる気が湧いたら、ということにさせてください…。

論文では、高階関数がプログラムのモジュール化に役立つ理由について「データ型の詳細に関する知識を高階関数の中に局所化できる」と述べているが、これを逆に言うと「特定のビジネスロジックを実装した関数を、それを適用する(データ型の)文脈から切り離せる」ということになる。

で、この考え方を推し進めると、一例として「抽象的な Future」が述べているような、「本番用の非同期実行の文脈」と「テスト用の同期実行の文脈」を同じコードで切り替えるみたいな仕掛けが実現できるようになる、というわけですね。

*1:分かってる人向けの言い方をするなら、モノイドの単位元と二項演算。

JJUG ナイトセミナーで Reactive Streams について発表しました

6月24日の JJUG ナイトセミナーで「Reactive Streams 入門」のタイトルで発表させて頂きました。最近話題の Reactive Programming、気がついたら一万人以上が署名している Reactive Manifesto、そして Java 9 で標準化という話が進んでいる Reactive Streams をまとめて俯瞰してみました、という感じの内容になっています。

かなり戦々恐々だったのですが、思いのほかご好評をいただきとてもとてもほっとしています。発表の機会を与えて下さった JJUG スタッフの皆様、会場をご提供頂いたオラクル様、発表を聴いてくださった参加者の方々、ありがとうございました。

発表でも触れましたが、"Reactive" という概念が何を指すかについては大きな混乱があり、様々な論者が異なる定義を提唱しているのが現状です。一方で、そうした定義の背景には、それぞれに体系的な知見や学術的な議論の積み上げがあるのも確かで、その辺をちゃんと掘り下げた解説を書いてみたいなぁ、と思っていました。

そんなわけで、この半年ほど継続的に資料を収集したり、V2 にアップデートされた Reactive Manifesto の翻訳をやったりしていました。構成については、記事を書くことを念頭に以前からぼんやりと考えてはいたのですが、今回の発表準備にあたって参考にした、英語版 Wikipedia「データフローを記述する宣言的なプログラミングモデル」「その実行モデルを実装したランタイム」という定義を軸に据えると、Reactive の名を冠した要素技術群をそこそこ総括的に整理できるのではないか、と考えて作ったのがこのスライドになります。

このテーマについては、まだまだ考えるべきことも多そうですし、今後も継続的に研究していきたいと思っています。改めて、今回は貴重な機会を頂きありがとうございました。

余談1

たしかに、次に使う個数を書いたモノを前工程に送るのって、完全にカンバンだなぁ。日本の製造業のプラクティスがまた世界を変えてしまった(違う。

余談2

今回の発表では、Reactive Programming のような非同期プログラミングについて、私が最近考えている『プログラミングモデルについての議論は概ね決着がついていて、焦点は「いかに高機能なランタイムを提供するか」という所に移りつつあるのではないか』という話を入れてみましたが、さて、実際のところどうなんでしょうか…。みなさんはどう思われますか?

これはずっとそうだと思うのですが、特に昨今、UI プログラミングやマイクロサービスといった文脈で非同期プログラミングの需要が高まる中でも、「非同期を同期的な文法で書きたい」というニーズは非常に根強いものがあります。

しかし、以前に「マイクロサービスが Scala を選ぶ3つの理由」という記事でも書いたように、特に分散システムの文脈では(有名な「分散コンピューティングの落とし穴」が述べるように)、レイテンシや処理の失敗、ネットワークの不安定性、あるいはそれを補うための運用監視といった話題は無視できません。過去に、「同期的な文法で非同期プログラミングができる」というコンセプトを打ち出したプログラミングモデルが大体失敗に終わったのは、そういった事情でしょう。

そう考えると、「同期プログラミングにとっての異物」をプログラマの目から隠してしまうのではなく、少し考え方を変えて明示的に扱った方が、最終的には幸せになれる気がしてきます。そして、Future/Promise や(Rx の)Observable のような関数型インタフェースは、非同期な実行モデルに基づくデータフローを記述する上で、優れた抽象化を提供してくれます。Reactive Programming が、多くの場合で関数型 Reactive Programming (FRP) の同義語として扱われるのは、そうした抽象化がもたらす利便性が大きな理由でしょう。

そんなわけで、本来であれば、近年の Reactive Programming の実践は「関数型プログラミング」と極めて密接な関係があります。しかし、今回は大前提として Java ユーザの方に向けた発表なので、そういった話題は基本的に除外することを心がけました。実際、スライドを見ていただければお分かりになると思いますが、「関数型とは何か」という議論に立ち入らなくても Reactive Programming を理解し活用することは可能です。

一方で、これらのライブラリをより効果的に活用したいと望むなら、関数型の考え方を調べておくと非常に役立ちます。この手の話に関心がある方は、以前このブログで書いた「関数型プログラマのための Rx 入門」シリーズをご覧になってみてください。

Reactive Streams が 1.0.0 になった

以前に紹介した Reactive Streams 仕様が 1.0.0 になりました。リリース文はこのへん

okapies.hateblo.jp

以下、Twitter に書いた感想をぺたぺた。途中で言及してる「つらみ」ってのはこのへんの議論とか瀬良さんのこの記事の話ですね。改めて読み返すと「解消される」ってのは言い過ぎだった(他にも課題はいっぱいある)けど、まぁ一つ障壁が取り除かれつつあるよなーという。

ReactiveX と「普通のやつらの上を行け」の意外な関係

これは「関数型プログラマのための Rx 入門」の補足記事です(タイトル変えた)。

前編後編とお送りしてきたこの記事だが、特に後編について「何を言ってるのか分からん」というコメントを何人かの方から頂いた。…なんというか、ごめんなさい

繰り返しになるが、Rx を使う上で関数型プログラミングの知識は必ずしも必要ではないし、むしろ(関数型のコンセプトが基礎にあるのに関わらず)知らなくても使えるようになっている。ライブラリの作者たちは「過度な抽象化は害になる」ということを弁えているのだろう。

しかし、Rx と関数型プログラミングの関係を把握しておくと、非同期データストリームのビルディング・ブロックの作り方について大いに視野が広がるだろう。もし、貴方がこの記事の前提となる「関数型」のパラダイムに興味をお持ちなら、まずは関数プログラミング実践入門」をお勧めしたい。

本の内容そのものは Haskell を前提にしているが、関数型の重要なコンセプトが一通り紹介されているので、今回の記事で出てきたキーワード(高階関数、代数的データ型、モナド、…)が属する世界観を概観するのに良いと思う。

また、このテーマに本気で取り組みたい初学者の方には、つい先日に発売されたばかりのScala関数型デザイン&プログラミング」を併せてお勧めしたい。

この本は、かねてより国内外で高い評価を得ている "Functional Programming in Scala" の日本語訳になる。ざっと見た感じ非常に「歯ごたえがある」感じだが、「関数型でプログラムを組み上げる方法」を基礎から丁寧に解説しており、演習問題も充実しているので、一冊読み通すとかなり力がつくのではないかと思う。

以下、後編を書いた後に気付いた話について少し補足。題して「Rx と『普通のやつらの上を行け』の意外な関係」

Observable の由来

後編で延々と書いたように、Reactive Extensions (Rx) の ObservableIterable の双対になっている。

() => (() => T)     // Iterable[T]
(T => Unit) => Unit // Observable[T]

では、形式的な説明はそれでいいとして*1、実際のところ Observable のアイデアはどこから来たのだろうか?

Erik Meijer の以下の投稿によれば、Observable は Rx の前身である Volta プロジェクト*2で非同期呼び出しをうまく扱う方法を探している時に見出したものだという:

We started working on IObservable/IObserver a long time ago when we were trying to make asynchronous calls that arose from tier-splitting palatable. Initially we used just the continuation monad but then discovered the beautiful duality with IEnumerable and IEnumerator.

F# vs. Rx: Msdn forums - Reactive Extensions (Rx)

そして、これは継続モナド (continuation monad) から着想を得たものであるらしい。*3

class Cont[R, +A](val runCont: (A => R) => R) { ... }

継続渡し形式の関数 (CPS function)

上記の継続モナド Cont が保持している関数 runCont: (A => R) => R継続渡し形式 (Continuation Passing Style; CPS) の関数という名前で呼ばれている。そして、後編で導出した Observable を表す「引数に渡されたコールバック関数に値を渡して実行する高階関数」も同様に CPS 関数だ(RUnit を適用してみよう)。

(A => R   ) => R    // runCont
(T => Unit) => Unit // Observable

継続渡しの「継続」とは、ここでは CPS 関数に渡されるコールバックを指している。CPS の詳しい説明は下記に挙げたページを見てほしいのだが、簡単に言うと「関数を呼び出して、戻ってきたら『続きの処理』を実行する」代わりに「関数に『続きの処理』を渡して呼び出し、その関数の最後で実行してもらう」というやり方だ。この「続きの処理」を継続と呼ぶ。

継続渡しで「普通のやつらの上を行け」

一般に「継続」は扱いの難しいプログラミングコンセプトとされていて、あまり積極的に活用されることがない(と思う)。では、なぜ非同期呼び出しの文脈で継続が出てくるのかというと、「処理をある所で中断してコンテキストを保存し、続きの処理が再開されるときに受け渡す」というパターンが CPS の考え方にバッチリはまるからだろう。以下、「なんでも継続」から引用:

ポイントは、外部の処理を呼びたいのだが、呼び出して戻り値を受け取るという形式が使えないケースにある。

例えばユーザインタフェースだ。処理の途中でユーザーに何か入力を促し、その結果を使って処理を続けたいことは良くある。しかし多くのGUIプログラミングでは、ユーザーの入力を受け付けるためには一度GUIのイベントループに戻らなければならない。したがって、プログラマは 処理をユーザーの入力の前にやる処理Aとユーザーの入力の後にやる処理Bに分けて、

 1. 処理Aの最後に入力ウィンドウをポップアップし、イベントループに戻る

 2. 入力ウィンドウの "OK" ボタンが押されるイベントが発生した時に 処理Bが呼ばれるようにする。

という具合にコーディングしているはずだ。この時、まさに処理Bは処理Aの「継続」なのだ。(Webアプリケーションにも全く同じ原理が使えることを指摘しておこう。 ユーザーからの入力が必要になった時、Webアプリケーションは一度入力フォームを吐き出してhttpサーバに制御を戻さなければならない。「普通のやつらの上を行け」Paul Grahamが述べているYahoo! Storeの システムはまさにこの技術を実装している。

つまり、歴史的にも意味的にも、Observable はまさにユースケースを非同期データストリームに絞って扱いやすくした継続モナドだということになる。

アカデミックな知識が MUST になる時代?

この「なんでも継続」の記事は(ブクマを見る限り)少なくとも 2006 年頃からあって、私も何度か目を通していた。のだが、正直なところ、今回記事を書くために読み返すまで UI や Web アプリへの応用という話は完全に忘れていた。

ぶっちゃけると、00 年代後半(?)にウェブ界隈で何度か不動点コンビネータの話がバズっていた頃、Yコンビネータの話題に関連してこの記事を読んでいるはずなのだが、当時は「いかにもギークの好きそうな頭の体操」として受け止めていて、応用の可能性についてはろくに考えが及んでいなかったと思う。

とかく、こうしたアカデミックな形式知は「小難しくて実用性がない」として軽んじられがちだ。しかし、関数型プログラミングしかり、こうして積み上げられてきた知見が実践的な問題に取り組むためのフレームワークとして活用されるケースは増えているし、海外の新しい OSS の動向を見るに、今後ますます増えていくだろうという感想を持っている。

全てのソフトウェア技術者がこうした方面の知識を習得すべきだとは思わない。しかし、個人的な感想としては、それぞれの現場で新技術の開発や選定に関わるリーダーや、エバンジェリストを自認するオピニオンリーダーにとって、このような学問的知識の習得が MUST になる時代はそう遠くない、という予感は日々強くなっている。

*1:ちなみに、Meijer は Rx の公開前にアップされた紹介動画の時点で既に双対性についての議論を披露している。この双対おじさん、筋金入りである…。

*2:Rx の電気ウナギのアイコンは、Volta のものをそのまま引き継いでいるらしい。

*3:Scala でのコード例は kmizu さんのコードから引用。