読者です 読者をやめる 読者になる 読者になる

「AngularJSリファレンス」という本を書きました

AngularJS book

金井さん(@can_i_do_web)と吉田さん(@teyosh)との共著でAngularJSリファレンスという書籍を執筆しました。

AngularJSリファレンス

AngularJSリファレンス

どのような本か?

この本はAngularJSの脱初心者〜中級者向けを目指して書きました。
公式ページのチュートリアルやドットインストールの入門講座をこなして、ある程度AngularJSを使えるようになった人が、その次に進みたいときに読むのに適していると思います。
また、AngularJSのソースコードを読まないと分からないような内容もいくつか書いているので、AngularJSを使い込んでいる人にも何かしらの新しい発見があるのではないかと思います。

本書はAngularJSの機能をひと通り網羅した内容になっています。
タイトルに"リファレンス"と入っていますが、単純に機能を紹介するだけではなく、serviceとfactoryの使い分けの方法を紹介したり、パフォーマンスを考慮した実装方法について解説していたり、カスタムフィルター・サービス・ディレクティブの実装例を数多く紹介していたりするので、ただのリファレンスにとどまる内容にはなっていません。

一方で、JavaScriptやHTMLの基礎、AngularJS向けのAPIサーバーの作り方、チュートリアル形式でアプリケーションを作る、というようなことは書いていないのでご注意ください。

みどころ

個人的におすすめなのは、Chap.11のテストとChap.12のセキュリティです。

Chap.11では、Karmaを使ったユニットテストと、Protractorを使ったインテグレーションテストについて解説しています。
ユニットテストについては、別の章で作成したサンプルコードを対象にして、フィルター・サービス・ディレクティブのテストを書くという形式で、Karmaの使い方、JasmineのAPI、モックの使い方や作り方などを解説しています。
インテグレーションテストについては、簡単なToDoアプリケーションを対象にしてProtractorの使い方を解説しています。さらに、ダウンロードできるサンプルコードには、本文中で解説しているテストコードの他に、各章のサンプルコードに対するインテグレーションテストを付属しています。本文中では紹介しきれなかったテクニックを使っていたりするので、ぜひ参考にしてみてください。

Chap.12は、間違ったことを記述するとまずいところなので、慎重になって書きました。ページ数の割には調査にもっとも時間がかかった章かもしれません。
この章では、どういう脆弱性があって、その脆弱性に対してAngularJSではどのような対策をしているのかを解説しています。さらには、制限の強い機能を先に説明し、できないことがあれば危険を理解した上で制限をゆるめていくという構成で解説しています。また、SCEを無効にするような危険な機能はあえて載せていません。

テストやセキュリティはAngularJSのコアの機能ではないため情報があまり多くないので、本書をぜひ役立ててもらえるといいなと思います。

AngularJSの対象バージョンについて

本書はAngularJSのバージョン1.2と1.3の両方に対応しています。

出版されるころにはAngularJS 1.3がリリースされているだろうという目論見で、AngularJS 1.3対応版として書いていました。その目論見通り、つい先日(8月29日)、AngularJS 1.3.0-rc.0(リリース候補版)がリリースされました。リリース候補の段階に入ったので、これから書籍の内容が通用しなくなるような変更は入らないと思っています。
ただし、ルーティング機能に関してはAngularJS 2.0向けに開発しているものをAngularJS 1.3にバックポートすると発表されているので、今後大きく変わるのかもしれません。

なお、AngularJS 1.3の新機能については、脚注に注意書きがしてあります。そのため、AngularJS 1.2で開発している人も問題なく読めるようになっています。(実際のところ、1.2と1.3はそれほど大きな違いはないですけどね)

ちなみに、個人的におすすめなAngularJS 1.3の新機能はng-strict-diと、ng-repeatの中で使えるasキーワードです。本書でも紹介しているので、ぜひ使ってみてください。

裏話的なもの

書籍の紹介はここまで。
ここからは裏話的なものを書きたいと思います。

執筆に至った経緯

昨年末くらいにQiitaにちょくちょくAngularJSネタを投稿していました。おかげさまでそれなりにストックされています。

この投稿がきっかけで「AngularJSの本を出す企画があるのですが参加しませんか?」と声をかけてもらえたようです。
つねづね機会があれば本を書いてみたいと思っていたので、即答でOKしました。

執筆に利用したツール

原稿はRe:VIEWで書いてBitBucketで管理していました。

今回はじめてRe:VIEWを使ってみたのですが、手軽に使うことができていいですね。中でもお気に入りなのが、外部ファイルを取り込む機能です。
サンプルプログラムはJavaScriptとHTMLで書いて、自動テストを通すようにしておきます。そしてGruntのコマンドをたたくと、サンプルプログラムを本文に取り込んで、HTMLやPDFを生成するようにします。
このような構成になっているので、テストを通っていないコードが本文に入ったり、サンプルプログラムのコピーミスを防いだりすることができました。

とか言っておきながら、ローカルにインストールしてあるProtractorのバージョンが古くて、テストが通らなくなっているところがありました。ごめんなさい><
詳細は正誤表のページをご覧ください。まもなく修正内容が反映されると思います。

雑感

執筆を引き受けたのはいいものの、短い期間でたくさん書く必要があったし、自分がどれくらいのペースで書けるのかも分かりませんでした。
そこで、毎日書いた文字数を記録して、目標に届かないようだったら土日に多めに時間をかけたりとかペース調整して書くようにしました。

f:id:ZOETROPE:20140831160410p:plain

その甲斐もあって何とか予定通り書き終えられました。でも平日の仕事が終わってからの時間と土日を使って、ほぼ休みなく2ヶ月くらい書いていたので、最後のほうは手首とか背中とかいろんなとこに痛みが…。もし次の機会があったなら、もう少しゆっくりめのペースで書きたいところです。

あと、ぼくの書く文章は誤解を与えないように注意しているせいか、くどい表現が多いみたいです。スカっと読める文章を書けるようになりたいですね。


というわけで、一度お手に取っていただけると幸いです。

AngularJSリファレンス

AngularJSリファレンス

AngularJSで始めるリアクティブプログラミング

RxJS AngularJS

最近はAngularJSの記事をQiitaにたくさん投稿していたのですが、久しぶりにこっちのブログにも書いてみます。

AngularJSでリアクティブプログラミングをするためのフレームワーク「ReactiveProperty for AngularJS」を作ったので、その紹介記事です。

リアクティブプログラミングとは

簡単に言うと、ユーザーの入力や外部イベントなどに応じて変化した値が、自動的に他の場所に伝搬されていき、それに反応する形で処理をするようなプログラミングパラダイムのことです。

詳細についてはこちらの記事が分かりやすいです。

複数のイベントを合成したり、時間を考慮した処理というのは従来記述がとても面倒なものでしたが、リアクティブプログラミングではそれらを宣言的に記述することができるようになります。

ReactiveProperty

先日こんなツイートがありました。

.NETでは、behavior中心のdata bindingと、events中心のRx*1が相補的になっているとのこと。
確かにそうですね。
そして、その間をつなぐReactivePropertyというライブラリがあります。

ぼくはこのReactivePropertyのコンセプトに感銘を受けて、とっても大好きなライブラリの1つなんですが、なかなか使う機会がないんですよね(涙)

ReactiveProperty for AngularJS

さて、先ほどのツイートの続きです。

AngularJSのデータバインディングもリアクティブプログラミングだけど、イベントに対する記述が手続き的になってしまうとのこと。
ならば、ReactivePropertyと同じ要領で、AngularJSのデータバインディングをRxJS*2につなげればいいのではないでしょうか。

というわけで、ReactiveProperty for AngularJSを作ってみました。

以降、具体的なコードで説明していきたいと思います。

データバインディング

まずは基本的なデータバインディングの例です。
入力した文字列を大文字に変換して、3秒遅延して出力するサンプルです。

<div ng-controller="BasicsCtrl">
    <input type="text" ng-model="currentText.value">
    <br/>
    {{displayText.value}}
</div>
var app = angular.module('app', ['rxprop']);
app.controller("BasicsCtrl", ["$scope",
  function ($scope) {
    $scope.currentText = new rxprop.ReactiveProperty($scope);
    $scope.displayText = $scope.currentText
      .select(function (x) { return x.toUpperCase(); })
      .delay(3000)
      .toReactiveProperty($scope);
  }]);

通常のAngularJSのデータバインディングと比べると、JavaScriptは複雑になってしまいました。
HTML側はバインドする変数名に .value が付いていますがそれほど違いはありません。

ちなみにC#版のコードと比べるとそっくりなのが分かると思います。

インクリメンタルサーチのレスポンス改善

上記の例ではそれほどメリットを感じないと思うので、もう少し実用的な例を紹介します。

AngularJSを使っていると、ng-repeatで列挙しているリストをフィルタで絞り込むということをよくやると思います。
でもこれ、リストの要素が増えてくると検索用のテキストボックスのレスポンスが悪くなりますよね。

そこで、RxJSのthrottleを使ってみます。
throttleはイベントが発生してから一定の時間待機して、次のイベントがこなければ後続に値を流すというものです。

例えば300ミリ秒以内に"abc"と連続して入力した場合は、"a"や"ab"ではフィルタリングを行わず、"abc"のみでフィルタリングを行うことができます。
これを手続き的に書こうと思うと難しいですが、ReactivePropertyでは以下のように書くことができます。

<div ng-controller="SearchCtrl">
    <input type="text" ng-model="searchTextInput.value">
    <div ng-repeat="result in results | filter:searchTextDelay.value">
        {{result}}<br />
    </div>
</div>
var app = angular.module('app', ['rxprop']);

app.controller("SearchCtrl", ["$scope",
    function ($scope) {
        $scope.searchTextInput = new rxprop.ReactiveProperty($scope);
        $scope.searchTextDelay = $scope.searchTextInput
            .throttle(300)
            .toReactiveProperty($scope);
        $scope.results = [/* 値の列挙 */];
    }]);

ReactiveCommand

続いてReactiveCommandです。

AngularJSにはWPFのCommandに該当する機能がないので、rp-commandとrp-parameterというディレクティブを新たに作りました。
次の例では最初はボタンが押せないようになっていますが、すべてのチェックボックスにチェックを入れてテキストボックスに文字列を入力するとボタンが押せるようになります。そしてボタンを押すとアラートが表示されます。

RxJSのcombineLatestは複数のイベントを合成するもので、いずれかのイベントが発行されたとき、他のイベントが最後に発行した値と合成して後続に通知をおこないます。

<div ng-controller="CommandCtrl">
    <input type="checkbox" ng-model="isChecked1.value">
    <input type="checkbox" ng-model="isChecked2.value">
    <input type="checkbox" ng-model="isChecked3.value">
    <input type="checkbox" ng-model="isChecked4.value">
    <input type="text" ng-model="currentText.value">
    <button rp-command="checkedCommand" rp-parameter="currentText.value">push</button>
</div>
var app = angular.module('app', ['rxprop']);
app.controller("CommandCtrl", ["$scope",
    function ($scope) {
        $scope.isChecked1 = new rxprop.ReactiveProperty($scope, {initValue: false});
        $scope.isChecked2 = new rxprop.ReactiveProperty($scope, {initValue: false});
        $scope.isChecked3 = new rxprop.ReactiveProperty($scope, {initValue: false});
        $scope.isChecked4 = new rxprop.ReactiveProperty($scope, {initValue: false});
        $scope.currentText = new rxprop.ReactiveProperty($scope, {initValue: ""});

        $scope.checkedCommand = $scope.isChecked1
            .combineLatest($scope.isChecked2, $scope.isChecked3, $scope.isChecked4, $scope.currentText, function (a, b, c, d, txt) {
                return a && b && c && d && txt;
            })
            .toReactiveCommand($scope);
        $scope.checkedCommand
            .subscribe(function (param) {
                alert("Execute! input = " + param);
            })
    }]);

さてこれ、C#版のReactivePropertyを真似てそっくりに作ってみたものの、AngularJSとの相性はあまりよくないと感じています。
なので、AngularJSに適したもっと良い方法を模索中です。

ReactiveCollection

ReactiveCollectionはイベントとして流れてきた値を、リストに追加して通知するための機能です。
例えば、WebSocketからログを受け取ってそれを表示するログビューアーみたいなものを、以下のように書くことができます。

<div ng-controller="LogCtrl">
    <div ng-repeat="log in logs.values">
        {{log.level}}: {{log.message}}<br />
    </div>
</div>
var app = angular.module('app', ['rxprop']);
app.controller("LogCtrl", ["$scope",
  function ($scope) {
    $scope.logs = webSocketAsObservable("ws://localhost:9999/")
      .selectMany(function (receiver) {
        return receiver;
      })
      .select(function (x) {
        return angular.fromJson(x.data);
      })
      .toReactiveCollection($scope);
  }]);

webSocketAsObservableは、RxJSでWebSocketクライアント - Qiitaを使っています。

AngularJSでは、WebSocketのようなAngularJSの管理外のトリガーで値を変更した場合、$scope.$apply呼んでDOMを更新する必要がありますが、ReactiveProperty for AngularJSでは自動的に$scope.$applyを呼び出すようになっています。

リストへの追加処理の改善

上記の例では、WebSocketで大量のメッセージが送信されてくると、JavaScriptの処理が間に合わずにブラウザが固まってしまいます。

RxJSには、指定した時間内に受け取ったイベントをバッファリングして、時間が経過したら後続に流すbufferWithTimeというAPIが用意されています。
また、ReactiveCollectionには、受け取った配列をフラットにしてリストに追加する flatten オプションを用意しました。

これらを利用して先ほどのコードを以下のように書き換えると、大量のメッセージを受信したとしても画面更新は1秒ごとになるので、ブラウザが固まりにくくなります。

var app = angular.module('app', ['rxprop']);
app.controller("LogCtrl", ["$scope",
  function ($scope) {
    $scope.logs = webSocketAsObservable("ws://localhost:9090/")
      .selectMany(function (receiver) {
        return receiver;
      })
      .select(function (x) {
        return angular.fromJson(x.data);
      })
      .bufferWithTime(1000)
      .toReactiveCollection($scope, {flatten: true});
  }]);

まとめ

駆け足で紹介してみましたが、いかがだったでしょうか?
ReactiveProperty for AngularJSには、他にもいくつかの機能があります。
ドキュメントはまだ用意できていないので、サンプルを参考にしてみてください。

また、ToDoMVCも用意しています。

ReactivePropertyについて詳しく知りたい場合は@neueccさんや@okazukiさんの記事が参考になります。

ReactiveProperty for AngularJSは、まだまだ開発段階ですが、自分のアプリに組み込んで育てていこうかなっと思っています。

リアクティブプログラミングは最初はとてもとっつきにくいですが、使いこなせるととても楽しいものです。
最近はいろいろなリアクティブプログラミングのライブラリが出てきているので、試してみてはいかがでしょうか。

*1:Microsoftの開発している、Reactive Extensionsというオープンソースライブラリ

*2:Reactive ExtensionsのJavaScript実装

数学初心者のための「型システム入門」入門

book

社内で「TaPLで殴りあう会*1」が開催されるというので、型システム入門(通称TaPL: Types and Programming Languages)を購入したものの、内容が難しくて序盤からまったくと言っていいほど読み進めることができませんでした。

型システム入門 −プログラミング言語と型の理論−

型システム入門 −プログラミング言語と型の理論−

  • 作者: Benjamin C. Pierce,住井英二郎,遠藤侑介,酒井政裕,今井敬吾,黒木裕介,今井宜洋,才川隆文,今井健男
  • 出版社/メーカー: オーム社
  • 発売日: 2013/03/26
  • メディア: 単行本(ソフトカバー)
  • クリック: 68回
  • この商品を含むブログ (7件) を見る

しかし、読めないままにしておくのは悔しいし、内容はとても面白そうなので、やはりちゃんと読めるようになりたい。
そこで基礎的な書籍とWebで情報収集してから再度挑戦したところ、なんとか読み進められるようになりました。

監訳者さんによると、序盤のいくつかの章と中盤以降の好きな章を読めば読んだことになるらしいので、ここでは11章までを読むために必要になった知識をまとめてみたいと思います。

準備

本書の序文には、以下のような前提知識が必要だと書かれています。

型システム入門を読み始める前に、これらの知識のうち不足しているものを補う必要があります。
僕は数学の知識に不安があったので、何冊か本を買って読むことにしました。

数学

最初はどういった数学の知識が必要になるのか分からなかったので、まず、ラムダ計算の勉強のしかた、プログラム意味論 - きしだのはてなを参考にして、論理学の本を読むことにしました。

「入門!論理学」は、一切記号を使わず論理学を説明していて非常に分かりやすい内容です。論理学の背景も解説してあって読み物としても面白い。ただ、導入としてはとてもよいのですが、型システム入門を読むための知識としてはちょっと足りないかな。

入門!論理学 (中公新書)

入門!論理学 (中公新書)

続いて「論理学」を読みました。「入門!論理学」と同じ著者の書籍ですが、こちらは記号も証明もきっちり出てきます。「入門!論理学」を読んでおくと構成が似ているので理解しやすいですね。

論理学

論理学

集合論についてもう少し理解したかったので、「ろんりと集合」も読んでみましたが、1つ1つの説明が薄いのでしっかり学びたい場合にはものたりないかも。また、内容とほとんど関係のないダジャレや先生と学生の会話がでてきたり、注釈がやたらと多くて章末にまとまっていたりして、ちょっと読みにくかったです。

離散数学に関しては、不足している知識を「離散数学―コンピュータサイエンスの基礎数学 (マグロウヒル大学演習)」で確認するくらいで十分だと思います。
もう少し学びたいのであれば、プログラムの動かし方の本 - きしだのはてなを参考に書籍を探してみるのがよさそうです。

あとは、型システム入門の内容に直接関係するわけではないのですが、「圏論勉強会」に参加した*2ことで、集合論や証明の手順などの理解の助けになりました。
資料と動画が公開されているので興味がある方は見てみるとよいかと思います。(1回2時間なので全部見るのは大変です)

高階関数プログラミング言語

本書は関数型プログラミング言語の知識を前提として解説が進むので、何かしら関数型プログラミング言語を知っておく必要があります。
サンプルプログラムはOCamlで記述されていますが、他の関数型言語(F#, Haskell, Scalaなど)しか知らなくても問題ありません。いずれかの言語の入門書レベルの知識があれば十分です。

また、本書を読みながら、サンプルプログラムを写経するか、自分の好きな言語で実装してみることをおすすめします。理解がぐっと深まります。
ちなみに僕はF#とFParsecを使ってコードを書いてます。

実践 F# 関数型プログラミング入門

実践 F# 関数型プログラミング入門

あとは、自分の実装の答え合わせをするために、OCamlのサンプルプログラムも動かせるようにしておくとよいですね。

コンパイラの基礎的な概念

コンパイラの知識としては、抽象構文木BNFなど、基本的なところさえ押さえておけば大丈夫です。

おすすめの書籍は思いつかないので、Webの入門記事を読むくらいで十分だと思います。「言語実装パターン ―コンパイラ技術によるテキスト処理から言語実装まで」でもいいかもしれませんが、この本は理論が少なめで実践的な内容で、型システム入門では使わないような高度な内容も出てくるので、ちょっと違うかも。

また、サンプルコードを書く際には書籍で定義された構文をパースしないといけないので、何らかのパーサジェネレータかパーサライブラリも使えるようになっておく必要があります。
サンプルコードはocamllex/ocamlyaccを使っていますが、lex/yacc系以外のものでも問題ありません。

第3~4章

準備が終わったらさっそく読み進めていきましょう。第2章は飛ばしてしまって構わないと思います。

で、3章から読み始めていくわけですが、ここをきっちり理解しようとすると難しくてくじけてしまうかもしれません。
なので、いまいち分からないところがあったらいったん飛ばして、4章で実装をしてから3章に戻ってくるといいと思います。
1ステップ評価と多ステップ評価がおおよそ分かれば大丈夫。

なお、実装の説明の中で抽象構文木のノードにinfo型が出てきます。これはパースエラーが発生したときの位置情報を格納するための注釈です。パーサの種類によっては不要なものなので、実装するときには無視してしまいましょう。

第5~7章

序盤の山場ですね。ラムダ計算、De Bruijnインデックス、シフト、代入など難しい概念がたくさんでてきますが、これらの概念はこの章以降もずっと使うのでしっかりと理解しておく必要があります。

ラムダ計算については「論理と計算のしくみ」が良書ということで買ってみましたが、この本もなかなか難しい。というわけで、下記のリンク先の記事をおすすめします。

僕は上記リンクの「ラムダ計算の基礎」の記事をいくつか読むことでなんとか理解できました。
ラムダ計算は読むだけではなかなか理解できないので、Church数の足し算や掛け算なんかは実際に手を動かして解いてみるのがよいと思います。

他には、ラムダ計算という言葉は出てこないものの「プログラミング言語の基礎概念」は、型システム入門と同じような内容を非常に分かりやすく解説しています。併読することで非常に理解が進みました。
オンラインの演習システムも用意されていて、パズル感覚で学べるのが楽しいですね。

プログラミング言語の基礎概念 (ライブラリ情報学コア・テキスト)

プログラミング言語の基礎概念 (ライブラリ情報学コア・テキスト)

7章まで進むと構文も少し複雑になってきます。僕は関数適用をうまくパースできなくて悩みました。ParsecやFParsecを使っているときは左再帰が解決できないので、chainlを使うといいみたいです。

第8~11章

ここまでくるとようやく型がでてきます。長かった〜(笑)
第8~11章では、特に新しい知識が必要になることはないように思います。

8章と10章は、4章と7章の実装に型を追加するだけなので、さくっと実装してしまいましょう。

9章は型の一意性と型安全性の定理および証明の解説です。定理自体はそんなに難しくないと思うのですが、証明を追いかけるのがつらい。証明は飛ばしちゃってもいいんじゃないかな・・・

11章では、これまでのプログラムにタプルやレコード、パターンマッチ、バリアントなどの構文を追加します。
パーサを書くのが大変ですが、だいぶプログラミング言語らしくなってきて楽しいですね。

俺達の型システム入門はこれからだ

11章まで読み終わったら、序章にある各章の依存関係グラフを参考にして、興味のある章へと進んでいきましょう。
部分型、再帰型、多相性、高階型などなど面白そうな内容ばかりですね。まだまだ先は長い。

*1:読書会です

*2:僕は第7回で脱落してしまいましたが

HTML5モバイルアプリケーションフレームワーク Sencha Touchパーフェクトガイド

Sencha book

会社の同僚達がSencha Touchの本を書きました。
僕自身はSencha Touchをほとんど触っていないのですが、せっかくなので読んでみました。

HTML5モバイルアプリケーションフレームワーク Sencha Touchパーフェクトガイド

HTML5モバイルアプリケーションフレームワーク Sencha Touchパーフェクトガイド

感想

本書の想定読者としては、ある程度HTML5JavaScriptを知っていることが前提になっています。HTMLやJavaScriptの基本的な文法の説明は出てきません(SassとCompassについては詳しめの解説がありました)。

本書は3部構成になっており、第1部は簡単なチュートリアル、第2部はSencha Touchが備えている基本機能の解説、第3部ではチャートやDropBox連携などのより実践的な内容を解説しています。

Sencha TouchはロジックもビューもJavaScriptで実装していくフレームワークなのですが、JavaScriptなのにプロトタイプベースではなくクラスベースのオブジェクト指向プログラミングができるようになっています。独自のクラスシステムを備えており、継承やミックスイン、シングルトンなど、JavaScriptの言語的に不足している部分を補っています。ここまでくると、ただのWebフレームワークと言うよりも言語拡張と言ってもいいかもしれませんね。
レイアウトの表現にはHTMLやXMLを使わずJavaScriptのオブジェクトで表現し、細かなデザインをSass/Compassで作るというのも面白いです。
また、フレームワークには規約がたくさんあります(setterの前後に呼び出されるメソッド名はapply/update+オプション名にするなど)。Railsの影響を強く受けてる印象です。
さらには、リストのグルーピングや、PullRefresh(リストの一番下でぐっと引っ張って更新するやつ)のようなUI、ネイティブアプリとしてパッケージングする機能など、モバイルアプリ特有の機能を多く備えているのも特徴的だと思いました。

こういった言語拡張やレイアウトの書式、フレームワークの規約を知らないとアプリケーションを書くことは難しいと思うのですが、現状では日本語で本書のように体系立てられた情報はほとんどないようです。
そういうわけでSencha Touchのアプリケーションを開発する人にとって本書は大きな助けになると思います。

MVCの説明について

さて、本書はSencha Touch開発に役立つ良書だと思いますが、MVCパターンの説明で少し気になる点がありました。
本書では、モデル層をデータの入れ物(エンティティ)であるという説明をしているのですが、そういう分け方をするとコントローラが肥大化してしまう可能性があります。
第3部では「ビジネスロジックをモデルとコントローラのどちらに記述するか判断できないためサービス層を導入する」という説明もあるのですが、MVCはプレゼンテーションとドメインロジックを分離するためのものなので、ビジネスロジックはモデルに入れるべきですね。その上で、モデルをデータアクセス層とサービス層に分ければよいと思います。
特にSencha Touchには、プロファイルというデバイスごとにビューやコントローラを切り替えるためのステキな機能があるので、なおさら共通処理はモデルに入れてコントローラを切り替え易くしておくべきです。
Sencha Touchが用意しているベースクラスの名前が「Ext.data.Model」になっているので勘違いを生じやすくなっている影響もありそうです。(RailsCakePHPも同様の問題が指摘されていたりしますね http://d.hatena.ne.jp/MugeSo/20121224/1356345261

MVCの話はネット上でも定期的に話題になりますが、MOVEは望まれなかった子 - the sea of fertilityが分かりやすくておすすめです。
SQLアンチパターンの「24章 マジックビーンズ(魔法の豆)」でも、「モデルがアクティブレコードそのものになる」というアンチパターンが紹介されていました。

F#で写経!?

本書を読みながら写経しようと思い、WebSharperのSencha Touch拡張を使ってみました*1
これを使うとF#で書いたコードが、Sencha TouchのJavaScriptコードに変換されます。

Visual Studioで型安全にさくさくコードが書けるのがステキ!と思っていたのですが、生成されたコートがSencha Touch 2系に対応していませんでした。
WebSharper自体はオープンソースなんですが、Extensionはソース公開もされていないですし。残念。

*1:普通にJavaScriptを書いても面白くないですからね

TypeScriptでSencha Touch開発に挑戦

Sencha TypeScript IDEA

前回に引き続き、Sencha Advent Calendar 2012の20日目の記事です。

Sencha Touch(Ext JS)では、HTMLをあまり書かずにJavaScriptをバリバリ書く必要があります。
しかし、個人的には静的な型付けのないJavaScriptはあまり書きたくないんですよね。

コンパイルしてJavaScriptを生成する系の言語は、CoffeeScript、Haxe、Dart、JSXなどいろいろありますが、やっぱりC#の開発者であるヘルスバーグ先生が手がけたTypeScriptを触らないわけにはいきません。

TypeScriptはまだ発展途上ではありますが、周辺ツールも整備されているようですし、バージョン0.8.3ではGenerics対応もあるとのことで、今後が非常に楽しみな言語です。


というわけで、Sencha TouchのアプリをTypeScriptで書くための手順を紹介したいと思います。
なお、本記事の内容は実用にはもう一歩*1というところなので、その点はご注意ください。

TypeScript環境の構築

前回と同じ環境を利用します。追加で以下のものをインストールします。

  • Node.js : 0.8.16
  • TypeScript : 0.8.1
  • JSDuck : 4.5.1


まず、最新のNode.jsをインストールします。(Ubuntuのデフォルトのリポジトリだと0.6という古いバージョンしかないので、リポジトリを追加してあげましょう。)

sudo add-apt-reposiotry ppa:richarvey/nodejs
sudo apt-get update
sudo apt-get install nodejs npm


続いてTypeScriptのインストールを行います。

sudo npm install -g typescript


Ext JSAPIリファレンスを生成するためのツールであるJSDuckもインストールします。(Rubyとgemが入ってない場合はあわせてインストールしてください。)

sudo gem install jsduck

IntelliJ IDEAでTypeScript

必須ではないですが、下記の記事を参考にしてIntelliJ IDEAでTypeScript開発を行う準備をします。

シンタックスハイライト、コード補完、関数定義へのジャンプなど、ある程度の機能は揃っているようです。

Sencha TouchのTypeScript用定義ファイル

TypeScriptからJavaScriptを呼び出す場合、アンビエント宣言*2の定義ファイルを用意する必要があります。拡張子が.d.tsになっているファイルですね。

jQueryやNode.jsの定義ファイルはすでに用意されているのですが、Ext JSのものは見当たりません。

と思って探していると、Ext JS用の定義ファイルを生成してくれるツールを見つけました。

C#+WinFormsで作られていますが、MonoがインストールされていればUbuntuでも動きます。


さっそく定義ファイルを生成してみたのですが、残念ながら生成したままのファイルは使えないようです。

exportやdeclareを追加したり、ドキュメントのないクラスのダミーを追加したり、moduleの階層構造を解決したり、いろいろ書き換えないといけない模様。

ちまちま書き換えてみたのですが、途中で面倒になったので自分でツールを書くことにしました。
Sencha Touch/Ext JSの開発者層を考えるとC#はないだろーと思いPythonで書き始めたのですが、せっかくなので途中からTypeScriptに書き換えました。


Node.jsで動作します。書きなぐりのコードでテストもありません><(TypeScriptのテストフレームワークは何を使うのが定番なんでしょうか・・・?)

初TypeScriptでしたが、C#Javaに慣れてればすんなり使えるように思えました。
といっても、今回は簡単なテキスト処理をしただけなので、TypeScriptの機能は全然使えてないんですけどね。

ちなみにTypeScriptでのコレクションの操作がよく分からなかったのでlinq.jsを使いました。LINQは空気。勝手知ったるライブラリがあると非常に助かります。

ExtJS2TypeScript

さて、このツールの使い方です。
まず、ExtJS2TypeScript.tsをコンパイルしてJavaScriptのファイルを生成します。

tsc ExtJS2TypeScript.ts


次にJSDuckを使ってSencha TouchのJSON形式のドキュメントを生成します。最初のオプションにSencha Touchのソースコードのディレクトリを指定、最後のオプションに出力先のディレクトリを指定します。

jsduck sencha-touch/src --builtin-classes --warnings=-no_doc,-dup_member,-link_ambiguous \\
  --external XMLHttpRequest --ignore-global --pretty-json --export=full --output jsondocs


生成されたJSONドキュメントのディレクトリを指定して、ExtJS2TypeScriptを実行します。出力結果はExt.d.tsというファイルになります。

node ExtJS2TypeScript.js ../jsondocs


生成したExt.d.tsをTypeScriptでコンパイルしてみると、プロパティと同じ名前の関数があるよとか怒られます。

Function or method 'getGroupString' already declared as a property
Duplicate identifier 'items'

とりあえずエラーがでなくなるまでコメントアウトしてしまいましょう。(本当はちゃんと対応しないといけないのですが・・・)

Sencha Touchアプリのビルド

いよいよSencha TouchアプリをTypeScriptでコンパイルします。

まず、前回の記事の中でSencha Cmdで生成したプロジェクトのディレクトリ(app.jsと同じ場所)に、Ext.d.tsをコピーします。

次にapp.jsをapp.tsというファイル名に変更し、先頭に下記の記述を追加します。

/// <reference path='Ext.d.ts'/>

JavaScriptのコードは、ほぼTypeScriptのコードとして動くので、コードの修正は不要です。


そして、app.tsをコンパイルしてみると・・・

Supplied parameters do not match any signature of call target

残念ながらエラー。

JavaScriptでは、関数で定義されている引数を渡さないことでオーバーロードのようなことを実現しています。

例えばExt.Loader.setPathという関数は、以下のように2種類の書き方ができます。

Ext.Loader.setPath('Advent','app');
Ext.Loader.setPath({
    'Ext': 'touch/src',
    'Advent': 'app'
});

しかし、Ext.d.tsでは次のような宣言しかしていません。

export interface LoaderStatic {
	:
    setPath(name:any /*String/Object*/, path:String) : Ext.LoaderStatic;
	:
}

TypeScriptにはオーバーロードがあるので、下記のように書き換えてあげます。

export interface LoaderStatic {
	:
    setPath(name:String, path:String) : Ext.LoaderStatic;
    setPath(name:any) : Ext.LoaderStatic;
	:
}

このあたりは自動化が難しいので手作業で修正することになります。つらい・・・(ドキュメントにはString/Objectと書いてあるので、これを考慮してオーバーロードすればよいかも?)

とりあえず、Sencha Cmdで生成したプロジェクトのひな形が動くように修正したExt.d.tsを置いておきます。(使っていない関数のオーバーロードは解決していません)

これでapp.tsをコンパイルするとapp.jsが生成されて、アプリが動くようになります!

TypeScriptのデバッグ

さて、JavaScriptを生成する系の言語で問題になるのが、生成後のJavaScriptを見てデバッグしないといけないことです。

TypeScriptから生成されたJavaScriptは比較的読みやすいとはいえ、できればTypeScriptのソースを見ながらデバッグしたい。

そこで、生成されたJavaScriptともとになったソースの関連付けを行うための技術としてSource Mapsというものがあります。

TypeScriptではコンパイルオプションに-sourcemapをつけることでapp.js.mapというファイルが生成されます。

tsc -sourcemap app.ts

生成されたjsファイルに下記のような記述が追加されていますね。

//@ sourceMappingURL=app.js.map


さぁ、IntelliJ IDEAでデバッグするぞ!と思ったのですが、現状のIntelliJ IDEAではSource Maps対応にまだ問題があるようです。
現在対応中のようなので楽しみにして待ちましょう。

おわりに

TypeScriptでSencha Touch開発する方法について紹介しました。
定義ファイルを用意するのはちょっと大変(もう全部anyって書いといてもいいんじゃないかという悪魔のささやきが・・・)ですが、今回作ったツールを使えば何とかなりそうな感じでもあります。

ただ、Sencha Touch(というかExt JS)は、引数に連想配列を渡すような関数が多いので、型を定義することでどれほどのメリットがあるのかまだ分かりません。もう少しコード書いてみないと何とも言えないです。


そして、Sencha Advent Calendarは無事に全枠が埋まりましたね!完走まであと少し!!

*1:生成した定義ファイルを手動で直す必要があるとか、関数のオーバーロードを解決してないとか

*2:C言語のプロトタイプ宣言のようなもの

IntelliJ IDEAでSencha Touchでの開発を始めよう

Sencha IDEA

本記事はSencha Advent Calendar 2012の15日目の記事です。

実は先日転職をして、12月からJava/JavaScriptをやってる会社で働いています。

これまでC#の開発ではReSharperを愛用していたので、JavaJavaScriptの開発にはやっぱりIntelliJ IDEAを使いたいよね。ということで、IntelliJ IDEAでSencha Touchアプリの開発を始める方法を紹介したいと思います。*1


IntelliJ IDEAとは、多言語に対応した統合開発環境(IDE)です。*2
JavaScriptでの開発にはEmacsvimを使っている人が多いとは思いますが、統合開発環境には補完機能、デバッグ機能、バージョン管理機能との連携、ブックマーク機能などなど便利な機能がたくさん用意されているので、一度試してみると良いのではないでしょうか。

開発環境

さて、まずは開発環境を構築しましょう。

OSはIntelliJ IDEAとSencha Touchが動けば何でも構わないと思いますが、今回はUbuntu 12.04を使いました。

利用ソフトウェアは下記の通りです。

  • Java 7.0
  • IntelliJ IDEA 12.0 Ultimate
  • Sencha Touch 2.1
  • Sencha Cmd v3.0
  • nginx 1.1.19

まず、@kawanoshinobu さんの記事(http://kawanoshinobu.com/2012/12/tutorial-1/)を参考にして、Sencha Touch、Sencha Cmd、nginxのインストールを行います。

Ubuntuでのnginxのインストールは下記のコマンドで行います。

$ sudo apt-get install nginx

編集するnginxの設定ファイルのパスはMacとは異なって以下にあります。

/etc/nginx/sites-available/default


続いてIntelliJ IDEAのインストールを行います。JetBrainsのページからインストーラをダウンロードして実行します。

IntelliJ IDEAにはnginxプラグインもあるので、必要であればインストールします。(インストールしなくても特に問題ありません)

プロジェクトの作成

次に、Sencha Cmdを使ってプロジェクトのひな形を生成します。

Sencha Touchのライブラリのパス(ここでは~/apps/sencha-touch/としました)に移動して、下記のコマンドを実行します。

cd apps/sencha-touch/
sencha generate app Advent ../advent

nginxを起動して、ブラウザからアプリのページが見えることを確認してください。


次にIntelliJ IDEAを起動し、メニューからFile->New Project...を選択します。
たくさんのプロジェクトの形式がでてきますが、その中からWeb Moduleを選択します。

Project locationには、プロジェクトを保存するパスを指定します。好きなディレクトリを指定してください。
さらに、ダイアログの下にあるMore Settingsの領域を開き、Content rootに先ほどのSencha Cmdのコマンドで生成したファイルのパスを指定します。

以上でプロジェクトの作成は完了です。


それではさっそく、コードを書いてみましょう。
IntelliJ IDEAの最大の魅力はやはり何といっても優秀なコード補完機能。

関数の引数や戻り値の型を表示してくれるのはもちろん、どのブラウザに対応しているのか、どのJSファイルで定義されているのかといった情報まで分かります。
また、CamelCaseの頭文字での補完機能なども使えて非常に便利です。(例えば、getHogeBarという名前の関数であればghbと入力すると補完してくれます)
JavaScriptだけではなく、HTMLやCSSでも補完機能は使えます。

もちろん関数宣言へのジャンプや、リファクタリングなどの機能も充実しています。IntelliJ IDEAでは内部的にJavaScriptのコードを解析しているらしいので、静的型付け言語と変わらないくらいコーディングのサポートをしてくれます。

デバッグ

続いてアプリのデバッグ機能を使ってみましょう。

メニューからRun -> Edit Configurations...を開きます。

JavaScript DebuggingのRemoteを選択します。URLにアプリが起動しているアドレス(例:http://localhost:3333/advent)を入力し、Debugボタンを押します。

ブラウザは好みのものを指定します。ここではFirefoxを使います。

初めてデバッグを行った場合、下記のようなFirefoxの画面が表示されると思うので、プラグインのインストールを行ってください。


エディタの左側の領域をクリックすると、ブレイクポイントを設定することができます。アプリの実行を一時停止して変数の値を見たり変更したり、ステップ実行したりすることができます。

なお、JavaScriptのパスを解決できない時に、ブレイクポイントの箇所に×マークが表示されることがあります。

そんなときは×マークを右クリックして、Specify remote URL for 'XXXX'を選択しURLを正しく指定してあげましょう。

デバッガには他にもたくさん機能があります。以下の動画で紹介されているので、一度見てみるとよいと思います。

http://www.jetbrains.com/idea/training/demos/JavaScript_Debugger/JavaScript_Debugger.html


さて、これでバリバリSencha Touchアプリの開発ができますね!!

*1:JavaScriptやHTMLでの開発しかしないのであれば、IntelliJ IDEAよりもWebStormのほうが安くておすすめです http://www.jetbrains.com/webstorm/

*2:ひとりIntelliJ IDEA Advent Calendar(http://d.hatena.ne.jp/masanobuimai/20121201#1354324848)が詳しいです

ROSのC#クライアントと非同期プログラミング

C# Rx ROS

Reactive Extensionsを使って、ROSのC#クライアントを作りました。

ROS

ROSとは

ROS(Robot Operating System)とは、Willow Garage社が開発しているロボットアプリケーションのためのソフトウェア開発基盤です。

ROSについてざっくりと知るには、以下のページが分かりやすいです。

ROSでは、ノードと呼ばれるロボットのソフトウェアモジュールをネットワークを通じて連携させてシステムを構築するわけですが、そのためのフレームワークやビルド・パッケージングの仕組みを提供しています。

ロボット用とは言いますが、分散システムを構築するフレームワークなので、ロボット以外のアプリケーションにもいろいろと活用できそうです。

ROSのクライアントライブラリ

ROSのノード間で連携する機能としては、3種類のモデルの通信機能が用意されています。

  • Publisher/Subscriber型のメッセージ通信(Topic)
  • RPC型のメソッド呼び出し(Service)
  • データ共有機能(Parameter)

これらの通信を行うクライアントライブラリの実装指針が下記のページで示されています。

現在のところクライアントライブラリの実装としては、C++PythonLISPJavaLuaRubyなどがありますが、C#の実装はまだないようです。

と思っていたら、先日 roscs なるものがリリースされていました。これはC++のコードをC#でラップしていて、Linux+mono環境でのみ動作するものみたいです。

一方、RosSharpはすべてC#で実装していて、Windowsでしか動きません。(mono-reactive を使えば、Linux+monoで動くかもしれませんが未検証です)

ROSはLinux文化なので、Windowsでしか動かないRosSharpにどれほどの需要があるのか分かりませんが、WindowsのGUIからROSのノードを使いたいときや、Kinect SDKと組み合わせたいときには有用なんじゃないでしょうか。

非同期プログラミング

時代は非同期!

スマートフォンやタブレットなどの端末が普及し、ユーザにストレスを与えないレスポンスのよいアプリケーションを開発することが重視されてきています。
そういったアプリケーションを開発するためには、非同期処理が重要となるわけですがなかなか実装が難しい。

そのため、次期C#C# 5.0、Visual Studio 2012)ではasync/await構文がサポートされ、非同期処理が簡潔に書けるようになります。

また、Windows 8の新しいアプリケーション実行基盤であるWinRTでは、50msec以上時間がかかる可能性のある処理は、非同期APIしか用意されていないそうです。

というわけで、RosSharpでは通信周りなど時間のかかる処理については、すべて非同期呼び出しとなるように実装しました。

C#で非同期処理を行うための手段としては、Task Parallel Library(TPL)や、Reactive Extensions(Rx)などがありますが、1回きりの非同期メソッド呼び出しはTask、データが非同期に連続的に流れてくるようなところはRx、と使い分けるのが良いようです。RosSharpでもその方針に従って実装しています。

RosSharpのサンプルコード

では、RosSharpのコードを見てみましょう。
同期呼び出しによるサンプルは以下のようになります。

try
{
    // Nodeの生成。Nodeが生成されるまで待つ。
    var node = Ros.InitNodeAsync("/Listener").Result;
    
    // Subscriberの生成。Subscriberが生成されるまで待つ。
    var subscriber = node.SubscriberAsync<RosSharp.std_msgs.String>("/chatter").Result;
    
    // メッセージを購読
    subscriber.Subscribe(x => Console.WriteLine(x.data));
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

次に、TAP(Task-based Asynchronous Pattern)によるサンプルは以下のようになります。

従来のAPM(Asynchronous Programming Model)やEAP(Event-based Asynchronous Pattern)とかに比べると、ネストが深くならずにすむのでシンプルに書けますが、慣れてないと読み書きしにくいのではないでしょうか。

// Nodeの生成
Ros.InitNodeAsync("/Listener")
    .ContinueWith(node => // Nodeが生成されたら呼び出される。
    {
        // Subscriberの生成
        return node.Result.SubscriberAsync<RosSharp.std_msgs.String>("/chatter");
    })
    .Unwrap()
    .ContinueWith(subscriber => // Subscriberが生成されたら呼び出される。
    {
        // メッセージの購読
        subscriber.Result.Subscribe(x => Console.WriteLine(x.data));
    })
    .ContinueWith(res => // エラーが発生したら呼び出される
    {
        Console.WriteLine(res.Exception.Message);
    }, TaskContinuationOptions.OnlyOnFaulted);

最後に、async/awaitを使ったサンプルは以下のようになります。*1
同期型とほとんど同じ書き方になってシンプルですね。
なお、以下のプログラムはVisual Studio 2012 RCで動作確認していますが、まだリリース前で今後仕様が変わる可能性もあるのでご注意ください。

try
{
    // 非同期でノードを生成する
    var node = await Ros.InitNodeAsync("/Listener");
    // ノード生成が完了するまでは呼び出し元に処理を返す。

    // 非同期でSubscriberを生成する。
    var subscriber = await node.SubscriberAsync<RosSharp.std_msgs.String>("/chatter");
    // Subscriber生成が完了するまでは呼び出し元に処理を返す。

    // メッセージを購読する。
    subscriber.Subscribe(x => Console.WriteLine(x.data));
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

ちなみに、全部Rxで書いてみると以下のようになります。*2

Ros.InitNodeAsync("/Listener")
    .ToObservable()
    .SelectMany(x => x.SubscriberAsync<RosSharp.std_msgs.String>("/chatter").ToObservable())
    .Switch()
    .Subscribe(
        x => Console.WriteLine(x.data),
        ex => Console.WriteLine(ex.Message)
    );
非同期処理で気をつけるべきところ

さて、TaskやRxを使ってコードを書いているといろいろとハマったので注意すべき点を挙げてみます。(当たり前のことばかりかもしれませんが・・・)

  • TaskにしろRxにしろ、例外をどのように伝搬させるかをしっかりと設計すること。正常系だけ先に書いて、あとで例外処理を追加していくと可読性の低いコードになってしまいました。また、ハンドルしてない例外があると、GCが動いたときにファイナライザで例外が発生するので、どこに問題があるのか追いかけづらいです。
  • Rxではメッセージの購読の必要がなくなったらきちんと破棄すること。今回メモリリークを多数発生させてしまいました。
  • ロックのタイミングに注意すること。ロックの範囲が広すぎると非同期処理のメリットも減ってしまうので、できるだけロック範囲が狭くなるように考える必要があります。
  • RosSharpではあまり考慮できていないのですが、処理のキャンセルや進捗状況についても考える必要があります。

NuGet対応

ROSはOSと言うだけあって、ビルドやパッケージングするための基盤が用意されています。しかし、RosSharpはそのあたりにまったく依存していません。

その代わりと言っては何ですが、RosSharpはNuGet Galleryに登録してあるので、簡単に使い始めることができます。

IDLパーサ

ROSでは、トピックやサービスで利用したい型を、独自フォーマットのIDL(Interface Definition Language)ファイルで定義することができます。

今回は、そのためのパーサをF#+FParsecで作りました。

オフサイドルールのパーサに関しては、以下の記事のものを使わせていただきました。感謝。

リアライザのパフォーマンス

RosSharpはノード間でメッセージ通信を行う際に、当然メッセージのシリアライズ・デシリアライズを行います。
下記の記事を見て、RosSharpのシリアライザのパフォーマンスはどんなものかなと思ったので、試しに計測してみました。(ついでにCORBA実装のIIOP.NETも)

Serialize Formatter - Protocol Buffers 2.0.0.480
00:00:00.4463423
13MB
Serialize JsonSerializer - JSON.NET 4.5.7
00:00:07.2403286
38MB
Serialize JsonSerializer - JSON.NET 4.5.7 BSON
00:00:10.0504359
44MB
Serialize AutoMessagePackSerializer`1 - MessagePack for CLI 0.2-beta1
00:00:00.5075449
11MB
Serialize Object RosSharp 0.1.0
00:00:00.4581154
16MB
Serialize CdrSerializer`1 - IIOP.NET 1.9.3
00:00:01.2561850
22MB

Deserialize Formatter - Protocol Buffers 2.0.0.480
00:00:00.8277988
Check => OK
Deserialize JsonSerializer - JSON.NET 4.5.7
00:00:09.1964346
Check => OK
Deserialize JsonSerializer - JSON.NET 4.5.7 BSON
00:00:09.9433615
Check => OK
Deserialize AutoMessagePackSerializer`1 - MessagePack for CLI 0.2-beta1
00:00:05.5326335
Check => NG
Deserialize Object RosSharp 0.1.0
00:00:00.9619598
Check => OK
Deserialize CdrSerializer`1 - IIOP.NET 1.9.3
00:00:03.2890727
Check => NG

RosSharpはProtocol Bufferより少し遅いくらいですね。ただし、これはRosSharpがすごいということではありません。

RosSharp以外のシリアライザは、リフレクションを使ってクラス構造を解析しつつ、シリアライズを行っています。一方のRosSharpは、IDL(Interface Definition Language)ファイルを書いて、そこから特定の型専用のシリアライズ・デシリアライズ処理のソースコードを生成しています。

というわけでフェアじゃないです。むしろ、リフレクションしてるはずのProtocol Bufferがこの速度だということに驚きですね。

今後

Visual Studio 2012がリリースされたら、async/awaitで全面的に書き直して、Rx2.0にも対応させたいですね。
また、テストやドキュメントが不足しているので追加していきたいと思います。

あとは、プロトタイプだけ作って放置してあるReactiveRTMもちゃんと実装しようかな。

*1:この処理を呼び出すメソッドにはsyncキーワードを付与する必要があります。

*2:Rx2.0ではTaskとの連携が強化されるのでもう少し簡潔に書けるようになります。