first step to haxe/javascript
DESCRIPTION
TRANSCRIPT
目次
• 自己紹介
• Haxeとは
• なぜJavaScriptコードを生成したいのか
• Haxeと他のJavaScript生成言語
• Haxe/JavaScriptの開発環境
• Hello World
• 基本的な構文・構造
• JavaScriptとの連携
• Haxeならではの機能・構文
• まとめ
得意な技術領域
• ギョーム系DataGridの実装に定評があります。
• JavaScriptで2回
• Haxe/JavaScriptで1回
• Silverlightで1回
• ここ数年はクライアントサイド開発が中心。
• でもシステムアーキテクチャ設計がたぶん本業。
• キーワード
Windows 8, .NET, WinRT, MSIL, F#,
JavaScript, Haxe, TypeScript, Android,
分散システム, Cassandra, …
Haxeと私
• Haxe歴は1年ほど。
• わりと大きなプロジェクトにHaxeを投入。
• 大規模なギョームシステムにHaxeを採用してみた話 http://www.slideshare.net/terurou/haxe-15006171
• はてブ 200超え
• でもこのスライド、発表当日に実質2時間ぐらいで書き上げたヤツなんだよね…
• 今も開発継続中、Haxe部分だけで5-6万行程度。
• Pure JavaScriptで書いている部分も数万行規模。
• サーバサイドも数万から10万程度はあるはず。
Haxeとは
• JavaScriptコードを生成することができる プログラミング言語
• C#やJavaに似た「慣れ親しんだ」構文
• 詳細な説明は後ほど
• この資料ではHaxe 3.0を前提に解説を行う
なぜJavaScriptを生成したいのか
JavaScriptで開発するのが
しんどい
なぜJavaScriptで開発はしんどいのか
• 生産性・品質とメンテナンス性が両立しにくい。
• チーム開発しづらい。
• 学習コストが高い。
原因1: JavaScriptの言語仕様が良くない
• モジュールシステムが存在しない。
• ライブラリや実行環境(ブラウザ、Node.jsなど)によって書き方が大きく変わる。
• prototypeベースオブジェクト指向を筆頭にして、言語仕様が柔らかすぎる。
• 初心者がprototypeチェーンを覚えることが大変
• 「classのようなもの」の書き方がたくさんある。
• グローバルオブジェクトのprototype上書き問題
• グローバル汚染+varの付け忘れバグ
• strictモードを使えばある程度マシになるが…。
原因2: JavaScript界隈の文化が良くない
• 他の言語から見るとバッドノウハウに近いようなコーディングテクニックが氾濫している。
• 即時関数でスコープを限定化
• オブジェクトのnull判定
• 「単独varパターン」ってみんな使っている?
• そのほか色々…
• コーディングテクニックの流行り廃りが激しい。
JavaScripterほこたて
• テクニックを駆使しないと書くのがしんどい。
• JavaScriptの言語仕様上、仕方がない。
• テクニックを駆使しすぎると他人が読めない。
• スキル・好み・ライブラリ・実装時期などによって コードの差異が激しくなりすぎる。
• チーム開発や保守でオーバーヘッドが大きくなる。
• チームメンバを教育しても次に生かせる保障がない。
JavaScriptは大変ですね
素直にあきらめて
JavaScriptは生成しましょう。
JavaScriptコードを生成するプログラミング言語
2013年6月時点で現実的な選択肢
• Haxe
• TypeScript
• CoffeeScript
他にもjs_of_ocaml、Dart、JSXなどがあるが、ユーザ数やとりまく環境を考えると選択しづらい。
Haxe
• 慣れ親しんだ構文+高度な型システム
• 基本構文はC#やJavaに似ている。
• 多少冗長ではあるが、万人に読みやすい。
• OCaml由来の型システム
• 型推論、代数的データ型、パターンマッチなど
• 簡潔に型安全なコードを書ける。
• マルチプラットフォーム
• JavaScript以外の様々な言語・環境に出力可能。
• コンパイルが超速い
• 歴史が長く(2005年~)、安定している
TypeScript
• JavaScriptの構文拡張
• 静的型付け+型推論、クラス、モジュールなど
• 型システムはHaxeほど高度ではないが、モダンな 言語としては最低限の機能は備えている。
• 将来的には一番人気になりそう
• Microsoftが本気で開発・プロモーションしている。
• まだまだ安定していない
• 2012年10月公開。
• 2013年6月にver 0.9.0 alphaが出たばかり。
• 1.0以後もAsync/Awaitなどの、インパクトの大きい仕様追加が予定されている。
CoffeeScript
• 軽量構文に特化
• JavaScriptと比較してコード量が1/3程度削減できる
• Rubyの影響が強い構文
• 記述しやすい反面、人によって書き方がばらつく。
• 動的型付け
• 型安全なコードは書きづらい…。
• 安定しているが、先行きを心配する声もある
• TypeScriptと直接競合しており、世間の流行は 静的型付け言語に向いてきている。
• とはいえ現在も開発が活発なので、数年は大丈夫?
Haxeを選択する理由
• 型安全な方が良い
• 品質、メンテナンス性に直結する。
• どうせ事前コンパイルが必要なので、コンパイル時にできることが多い方が良い → Haxeが有利
• コーディングスタイルのばらつきを抑えたい
• CoffeeScriptは練度の高いチームなら良いが…。
• 今すぐに使える
• TypeScriptは1.0まで待った方が無難。
補足:型安全とは
• 自分も詳しくないので引用でお茶を濁す
https://twitter.com/camloeba/status/248734702062686208
補足:型安全なコードを書くと…
• UnitTestを簡素にできる
• 型で表現/制限できることは、コンパイルが通れば バグがないことが保障される。
• Haxeの型システムで表現できることは多い。
• コンパイルすること自体がテストであるともいえる。
• UnitTestより確実
• コンパイラが静的に解釈(100%網羅) vs 全てのコードパスを通るテストコード
• 人がテストを書く以上、テストケースにバグや漏れが発生する可能性がある。
Haxeコンパイラ
• Haxeプログラミングするためには必須。
• 現行バージョンは3.0(2013年6月時点)
• インストール
• Windows, Mac, Linux用バイナリを公式で配布。 http://haxe.org/download
• インストール時に難しいことは特になし。
エディタ・IDE
• おすすめ
• Flash Develop(Windowsのみ)
• Sublime Text(有償) + haxe-sublime-bundle
• 実用レベルだが少し動作が重い
• IntelliJ IDEA Ultimate(有償) + haXe plugin
• FDT(Eclilseベース、フリー版と有償版がある)
• 使ったことはないがイケてそう
• Vim + vim-haxe
• Vim + Vaxe
• Emacs + haxe-mode
Flash Developのインストール
• 公式ページ http://www.flashdevelop.org/
• インストーラの注意点
• .NET Framework 3.5が必要。
• Windows 8の場合は別途インストール fondue.exe /enable-feature:netfx3
• Java未インストールの場合、インストーラ起動時に 警告ダイアログが出るが、無視して構わない。
• Flex SDK, AIR SDK, Flash Playerは選択不要。
Flash Developのロケールを日本語に設定
• 日本語環境が良い場合は設定する。
• 後述の手順は日本語設定を前提にしている。
• 設定手順
• Tools→Program Settings
• 右上のFilter settingに locale と入力
• Selected Localeを ja_JP に変更
• Flash Developを再起動
Sublime Text + haxe-sublime-bundle
• Sublime Text
• 公式ページ http://www.sublimetext.com/
• インストール時に難しいことは特になし。
• haxe-sublime-bundle
• Sublime Package Controlからのインストールが楽。
• インストール方法と使い方 http://wbond.net/sublime_packages/package_control/installation
• Package Controlで haxe と入力して選択するだけでパッケージがインストールされる。
ブラウザ(デバッグ環境)
Haxeコードをそのままデバッグするには Source Mapsに対応したブラウザが必要
Chrome OK
Internet Explorer 未対応(IE11でも×)
Firefox v23で対応予定
Safari 未対応
Opera 未対応
ChromeでSource Mapsを有効にする
• 設定方法(初期値は無効)
• Developer Toolsを開く
• menu→ツール→Developer Tools もしくは Ctrl+Shift+I
• 右下の歯車アイコンをクリック
• Generalタブ→Sources→Enable source maps
基本的な流れ
• プロジェクト(.hxmlファイル)の作成
• hxml = Haxe標準の簡易ビルドスクリプト
• Flash Developの場合は、.hxmlファイルがなくても 開発できるが、他環境を考えると作成した方が良い。
• ここではFlash DevelopとSublime Textの手順を解説。
• プログラムを記述
• とりあえず alert() を表示するだけ。
• ビルド・実行
• ついでにデバッガを使ってみる。
プロジェクトの作成(Flash Develop)- 1
• Flash Developプロジェクトの作成
• menu→プロジェクト→新規プロジェクト
• Haxe/JS Projectを選択して作成
• パッケージは未入力、その他は任意設定で可。
プロジェクトの作成(Flash Develop)- 2
• ビルド設定の変更
• menu→プロジェクト→プロジェクト設定
• 書き出しタブ→コンパイルターゲット
• カスタムビルドに変更
• ビルドコマンドタブ →ビルド前に実行するコマンドライン
$(CompilerPath)¥haxe.exe $(ProjectDir)¥build.hxml
プロジェクトの作成(Flash Develop)- 3
• ビルドスクリプトの作成
• プロジェクトルート直下にbuild.hxmlを作成
• bin/index.html の修正
• build.hxml-jsで設定したJavaScriptファイル名を 指定する。
–main Main -cp src -js bin/main.js -debug
<script src="main.js"></script>
プロジェクトの作成(Sublime Text)- 1
• プロジェクトのディレクトリツリー
• プロジェクトファイルの作成
• menu→Project→Save Project As ...
• プロジェクトのルートディレクトリに保存
$project_root/ src/ bin/
プロジェクトの作成(Sublime Text)- 2
• プロジェクトファイルの編集
• menu→Project→Edit Project http://qiita.com/amay077/items/570beb096056d6f16f2a
• folders.older_exclude_patterns は好みで追加。
{ "folders": [ { "path":"." //, "folder_exclude_patterns": ["bin"] } ], "build_systems": [ { "name":"haxe", "working_dir":"${project_path:Default}", "cmd":["haxe", "build.hxml"] } ] }
プロジェクトの作成(Sublime Text)- 3
• ビルドスクリプトの作成
• プロジェクトルート直下にbuild.hxmlを作成
• bin/index.html を作成
• build.hxml -jsで設定したJavaScriptファイル名を 指定する。
-main Main -cp src -js bin/main.js -debug
<!DOCTYPE html> <script src="main.js"></script>
プログラムを記述
• src/Main.hxを作成
• haxeでは { を行頭に置くことが多い(C#風)
• Flash Develop、Sublime Text共にデフォルト設定
• 普通に行末に { 置くことも多いので好みで。
package; import js.Lib; class Main { static function main() { Lib.alert("hello"); } }
ビルド・実行
• ビルド
• Flash Develop
• menu→プロジェクト→プロジェクトをビルド
• F8
• ツールバーの歯車アイコン
• Sublime Text
• Ctrl + B
• 実行
• bin/index.htmlをブラウザで開く。
• alert()が表示されれば成功。
Chromeのデバッガの使い方
• Developer Toolsを開く
• menu→ツール→Developer Tools or Ctrl+Shift+I
• Sourcesタブの左上の をクリック
• ソースツリーが表示されるので、breakpointを仕掛けたい位置を設定
• ソースツリーペイン右上の をクリックすると ペインを開いたままにしておくことが可能。
スタートアップクラス(エントリーポイント)
• プログラム起動時に最初に呼び出される部分
• build.hxml -mainで指定したクラスの static function main() メソッドが該当 -main Main -cp src -js bin/main.js -debug
package; import js.Lib; class Main { static function main() { Lib.alert("hello"); } }
trace()
• trace() = console.log()
package; import js.Lib; class Main { static function main() { trace("hello"); } }
変数の宣言
• var 変数名 : 型名;
• var 変数名 : 型名 = 初期値;
• 型推論により、var 変数名 = 初期値; でも可。
var foo : Int; foo = 1; foo = "abc"; //コンパイルエラー
var foo : Int = 100;
var foo = 100;
基本型とリテラル
• Haxeではnullはどんな型の変数にも代入できる。
• 型安全の考え方とは矛盾があるので注意。
• 実行環境が動的型付けなので仕方がない。
型名 リテラル
Int 0, -123, 0xFF00
Float 123.0, .456, 12e-5
Bool true, false
String "abc", "hello,¥nworld", 'hello "world"'
EReg(正規表現) ~/[a-z]+/i
null
文字列リテラルの注意点
• シングルクォーテーションは変数展開を行う。
• いわゆるフォーマット文字列
• ダブルクォーテーションでは変数展開しない。
var foo = 123; trace('foo'); trace('foo = $foo'); trace('foo = ${foo * 100}');
配列と匿名オブジェクト
• 配列はJavaScriptとほぼ同じ
• 匿名オブジェクトもJavaScriptとほぼ同じ
var foo = [1, 2, 3]; foo.push(100); trace(foo[1]); trace(foo.length);
var foo = {name: "foo", value: 12345}; trace(foo.name); trace(foo.hoge); //コンパイルエラー foo.value = 100; foo.valueX = 123; //コンパイルエラー
Dynamic
• 変数の型にDynamicを指定するとなんでもできる。
• JavaScriptと同等の型制御(動的型付け)になる。
• コンパイラが型チェックできなくなるので注意。
• 以下のコードはすべてコンパイルが通る。 var foo : Dynamic = 100; foo = "abc";
var foo : Dynamic = { name: "foo", value: 123 }; foo.value2 = -200;
クラス
• クラス名は必ず先頭を大文字にする class Point { // インスタンス変数 var x : Int; var y : Int; // コンストラクタ public function new(x, y) { this.x = x; this.y = y; } // メソッド public function toString() : String { return "Point(" + x + "," + y + ")"; } }
newとメソッドの呼び出し
class Main { static function main() { var point = new Point(10, 20); trace(point.toString()); } } class Point { // 省略... }
プロパティ
• 詳細はhttp://haxe.org/ref/properties
class Foo { var valueField = 100; public var value(get, null) : Int; function get_value() { return valueField; } }
継承
class Foo { public function new() {} public function method() {} } class Bar extends Foo { public function new() { super(); } override public function method() { super.method(); } }
interface
interface IPoint { var x : Int; var y : Int; function toString() : String; } class Foo implements IPoint { public x : Int; public y : Int; public function new() {} public function toString() { /* ... */ } }
ジェネリクス
• クラスに型パラメータをつける
• 関数(メソッド)に型パラメータをつける
class Queue<T> { var items : Array<T>; public function enqueue(entry : T) : Void { } public function dequeue() : T { } }
var queue = new Queue<String>();
function foo<T>() { /* ... */ }
定数(inline)
• Haxeには定数(const)に該当するものはないが、inlineを使うことで代用可能。
• その名の通り、コンパイル時にインライン展開される。
class Main { static inline var PI = 3.14; static function main() { var r = 5.0; trace(r * r * PI); } }
package(namespace)とimport
• パッケージ名は必ず小文字のみで構成する。
• パッケージ名とパスは必ずマッチさせる。
• 詳細はhttp://haxe.org/ref/packages
// src/mypackage/sample/Foo.hx package mypackage.sample; class Foo { // ... }
import mypackage.sample.Foo; class Main { // ... }
アクセス修飾子(クラス・型)
• publicとprivateの2種類
• 記述を省略するとpublic
• privateを付けると、同じファイルからのみ参照可。
• 詳細はhttp://haxe.org/manual/modules
class Foo { // ... } // importしても参照できない private class Bar { // ... }
アクセス修飾子(フィールド・メソッド)
• publicとprivateの2種類
• 記述を省略するとprivate
• privateは他の言語のprotectedに該当
class Foo { var value1 = 100; private var value2 = 200; public var value3 = 300; public function new() {} function privateMethod() {} }
条件分岐
• if, switch, 三項演算子(cond ? a : b)
• switchのcaseには break は書かない。
• 全て式なので、代入式の右辺にもできる
if (foo > 0) { } else if (foo == 0) { } else { }
switch (foo) { case "abc": //... case "xyz": //... default: //... }
var foo = (cond) ? 100 : 0; var bar = if (cond) 100 else 0; var baz = switch (x) { case "abc": 100; default: 0; }
ループ構造
• for, while, do-while
• breakも利用可能
• forにはiteratorしか指定できないので注意
for (i in 0...100) { }
while (cond) { }
do { } while (cond);
例外
• どんな値でもthrowできる
• Haxeの文化的にはStringを投げること多い?
• でもデバッグが面倒なのでError投げた方が良いかも。
• finallyは存在しない
• 詳細はhttp://haxe.org/doc/cross/exceptions
try { throw "error"; } catch (msg : String) { } catch (ex : Dynamic) { }
標準API JavaScript関係の主要クラス
• http://haxe.org/api/js
• js.Browser
• windowオブジェクトのラッパー
• js.Lib
• alert(), eval()
• js.Cookie
• js.JQuery
• http://haxe.org/api
• haxe.Json
• haxe.Http
• haxe.Timer
DOM
• js.Browserクラスが起点となる
• http://haxe.org/api/js/browser
import js.Browser; var elem1 = Browser.document.getElementById("foo"); var elem2 = Browser.document.querySelector("div"); elem1.addEventListener("click", function (e) { trace("click"); });
通信(XMLHttpRequest)
• haxe.Httpクラス
• XMLHttpRequestのラッパー
• 生のXMLHttpRequestが必要な場合は、js.Browser.createXMLHttpRequest()を使う
import haxe.Http; var http = new haxe.Http("/test"); http.async = true; http.onData = function (data) {} http.onError = function (msg) {} http.request();
JSON
• haxe.Json
• JavaScriptそのままのAPI
import haxe.Json; var str = Json.stringify({foo: "bar"}); var data = Json.parse(str);
jQuery
• js.JQuery
• jQueryのextern
• jQueryをHTMLにインクルードしておく必要あり
• ただし、コンパイルオプション(build.hxml)に -D embed_js を追加すると自動で生成物にjQueryを埋め込む(はず、ソースを見ただけで未確認)
import js.JQuery; var btn = new JQuery("button"); btn.click(function (e) { /* ... */ });
JavaScriptライブラリのexternを作成
• 外部JavaScriptライブラリを使いたい場合、 extern(+typedef)さえ用意すれば、 コンパイラチェックとエディタで入力補完が。
• http://haxe.org/doc/js/extern_libraries
package; extern class ExtraComponent { public function new(); public var visible(default, null) : Bool; public function hide() : Void; public function show() : Void; }
インラインJavaScript
• untyped __js__()
• JavaScriptのif文を組み立てることも可能
• JavaScriptから戻り値を受け取る
untyped __js__("alert('hello')");
var count = 100; untyped __js__("for (var i = 0; i < count;++i) {"); trace("hello"); untyped __js__("}");
var text = "100px"; var num = untyped __js__("parseInt(text, 10)");
JavaScriptからHaxeコードの呼び出し
• @:exposeを付けると、グローバルにクラスが 公開される。
• コンパイル後は普通にJavaScriptライブラリとして 利用可能になる。
• exporseに文字列を指定すると別名で公開できる
@:expose class Foo { /* ... */ }
@:expose("sample.Bar") class Foo { /* ... */ }
代数的データ型(enum)とパターンマッチ
これのためだけに
Haxeを使っていると言っても
過言ではない。
代数的データ型(enum)とパターンマッチ
TypeScriptにはこれがないので
積極的に触る気が起きない…。
enumとswitch
• caseはenumの要素を全て書かないとダメ
• コンパイラが記述漏れをチェックしてくれる
enum TextAlign { Left; Right; Center; }
switch (align) { case Left: //... case Right: //... case Center: //... }
enumに値を持たせる
enum Color { Red; Blue; Green; Glay(level : Int); Rgb(r : Int, g : Int, b: Int); }
var orange = Rgb(0xFF, 0xA5, 0x00);
switch (color) { case Red: //... case Blue: //... case Green: //... case Gray(level): //... case Rbg(r, g, b): //... }
enumの再帰構造
enum Color { Red; Blue; Green; Glay(level : Int); Rgb(r : Int, g : Int, b: Int); Alpha(aplha : Int, color : Color); }
var color = Alpha(0xA0, Rgb(0xFF, 0xA5, 0x00));
enumとジェネリクス
• 関数型言語で頻出するOption/Eitherも定義可能
• http://d.hatena.ne.jp/mzp/20120613/jsx
enum Option<T> { Some(value : T); None; } enum Either<TLeft, TRight> { Left(value : TLeft); Right(value : TRight); }
enumでツリー構造(よくある例)
enum TreeNode<T> { Node(children : Array<TreeNode<T>>); Leaf(value : T); }
var root = Node(children: [ Node(children: [ Leaf("A"), Leaf("B") ]), Leaf("C") ]);
function visit<T>(tree : TreeNode<T>) { switch (tree) { case Node(children): for (x in children) visit(x); case Leaf(value): trace(value); } }
C
A B
パターンマッチ
• Haxe 3.0の目玉機能の一つ
• 使いこなすと非常に便利
• パターンマッチの使い方だけで20分ぐらいは 話せそうなので、今回は説明を省略…。 • http://haxe.org/manual/pattern_matching?lang=jp
その他(今回は説明を省略)
• 構造的部分型(Structural Subtyping)
• 型安全なダックタイピング。
• interfaceを使う機会がかなり減る。
• http://haxe.org/manual/struct
• using mixin
• C#の拡張メソッドそのもの。
• http://haxe.org/manual/using
• マクロ
• コンパイルタイムにゴニョゴニョする。
• ASTで弄れるので高度なゴニョりが可能。
• http://haxe.org/manual/macros
まとめ
• JavaScriptはいろいろつらい
• 特にメンテナンス性が低すぎる。
• Haxeを使えば大抵の問題が解決する
• 「なじみの深い」構文のおかげで、開発時に必要な 最低限度の構文を学習するコストも低い。
• Haxeの型システムやマクロなどをうまく使えば メンテナンス性とパフォーマンスを更に高められる。
• Haxeの構文・機能について書きたいことは たくさんあるが、全てを書ききれずに力尽きた。
• 公式ドキュメント見てね http://haxe.org/doc
ご清聴ありがとうございました