typescriptでenchant.jsのハローワールドやってみた
mizchi/enchant-with-typescript https://github.com/mizchi/enchant-with-typescript
coffeescriptで書いた時と同じく、interfaceを書いて継承してモジュール定義、という流れ
src/types/enchant.d.tsが定義ファイル。プロパティを網羅できてないが、typescriptでenchant.jsやりたい人には便利だと思う。
感想
HaXeでやったときよりすんなりいった印象。
ただ、割とどうしようもない部分でへんなことしないといけなくて、例えば
interface Window { enchant: Function; } window.enchant();
enchantをmoduleにしてしまったせいで、関数としてcallできない事に気づいた。
かわりにwindow:Window のenchantを借りてきてFunction型にして初期化関数の呼び出しをしている。
型のinterface書くの、脳みそ止めてもできるのでアニメみながらやるといいと思う。
上手く説明できないのでここにmain.ts置いておく。
///<reference path='types/enchant.d.ts'/> declare var enchant : enchant; // In this context, enchant is module so it is not callable. // Use window.enchant instead of 'enchant'. interface Window { enchant: Function; } window.enchant(); module App { export class Bear extends enchant.Sprite { constructor(){ super(32, 32); this.x = 8; this.y = 8; this.image = Game.game.assets['images/chara1.png']; var self = this; this.on('enterframe', () => self.update()); } update(): void { var input = Game.game.input if (input.right) this.x += 2; if (input.left) this.x -= 2; if (input.up) this.y -= 2; if (input.down) this.y += 2; } } export class Game extends enchant.Game { public static game: Game; constructor(){ super() Game.game = this; this.fps = 24; this.preload('images/chara1.png'); this.onload = () => { var bear = new Bear() this.rootScene.addChild(bear); var game = this; }; } } } window.onload = () => { var game = new App.Game(); game.start(); };
typescript触ってみた ついでにunderscoreのinstance定義ファイルを作ってみた
主に自分が一番使っているcoffee-scriptとの比較
underscoreのは練習がてら作ってGithubにおいた
mizchi/underscore.d.ts https://github.com/mizchi/underscore.d.ts
書き味
構文の硬さがJavaが10 Python6 Rubyが3という記事をどっかで見たけど、同じように当てはめると、JS8 typescript6 coffee2という感じ。
coffee-scriptの超ゆるふわスタイルに慣れてると結構辛い感じはあるが、自分多分Coffeeについてある程度極まってる感じなので、あんまり比較しないほうがよさそう。
高階関数(後述)が簡単に定義できるのでJS特有のパラダイムには素直に応えられる。
""" multiline text """ がない
デフォルト引数がない
最初にunderscoreの型定義ファイルを書いた理由
JSのスーパーセットなだけに、イテレーションが貧弱
Lambda
結局自分はfunctionと書きたくなくてcoffeescriptを使ってるフシがあるのだけど、どのぐらい省略できるか調べてみた。
自分自身を返す f(x) = x はこう書ける
(i) => {return i}
ブロックだと複数行書けるが、returnが必須
もしくはブロック省略して
i => i
こちらはreturnが要らない
CoffeeScriptと違って 引数なしの()は省略できない
[1,2,3].forEach((i) => { console.log(i); })
こんなふうに某関数型言語っぽく書くことも
var fuga: (number) => (string) => number = (a) => (b) => a * b.length; console.log(fuga(3)("dafa"))
JavaScriptのスーパーセット
CoffeeScriptの辛かった点である、「jsコピペしてすぐ動かない」問題が解決した。
とりあえずコピペしてから徐々に置き換えるという段階を踏める。
エディタ
宗教上の理由(一方が好きというかは一方が嫌い)でVSが使えないのだが、SublimeTextで書いていたが補完はなかった。
まあ簡単そうなのでそのうち誰かが作るだろう。
instance
公式リポジトリのサンプルに型ファイル情報が少しついてる
- express.d.ts
- jquery.d.ts
- jqueryui.d.ts
- node.d.ts
todomvcにはBackboneモジュールに対して型アノテーションを追加していて参考になる。
declare module Backbone { export class Model { constructor (attr? , opts? ); get(name: string): any; set(name: string, val: any): void; set(obj: any): void; save(attr? , opts? ): void; destroy(): void; bind(ev: string, f: Function, ctx?: any): void; toJSON(): any; } export class Collection { constructor (models? , opts? ); bind(ev: string, f: Function, ctx?: any): void; collection: Model; length: number; create(attrs, opts? ): Collection; each(f: (elem: any) => void ): void; fetch(opts?: any): void; last(): any; last(n: number): any[]; filter(f: (elem: any) => any): Collection; without(...values: any[]): Collection; } export class View { constructor (options? ); $(selector: string): any; el: HTMLElement; $el: any; model: Model; remove(): void; delegateEvents: any; make(tagName: string, attrs? , opts? ): View; setElement(element: HTMLElement, delegate?: bool): void; tagName: string; events: any; static extend: any; } }
curry化/ っぽくしたかったけど上手くいかなかった
var fuga: (number) => (string) => number = (a) => (b) => a * b.length; // it works console.log(fuga(3)("dafa")) // it doesnt work Function.prototype.proc = function() { var args, target, v; var __slice = [].slice; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; target = this; while (v = args.shift()) { target = target(v); }た return target; }; console.log(fuga.proc(3, "dafa"))
Function型にprocがない。prototypeを拡張したことを認識されていない。
誰か教えてください。
公式サイトのリアルタイムインタプリタなら通ったので、コンパイルオプションでどうにかなるかも
闇鍋勉強会#2いってきた + LTしてきた資料 #yaminabePG
いってきた
第2回 闇鍋プログラミング勉強会 : ATND http://atnd.org/events/29973
発表資料はgistだけどな!
○ JavaScriptを書きたくない話 https://gist.github.com/3726883
要は、いかにJS書かずに他の言語から生成できるか、どういうスキルの人がどの言語を選ぶかっていう話です
二日酔いでくたばってて遅れていったので、その場で作りました(サーセン)
あと家に帰ったら
https://github.com/manuel/wat-js
とかいう言語がまた増えてました。いまからいじる。
Backbone.Model の かっこいい使い方
IE8は死に、get, set 使わずに値を出し入れできて、ちゃんとchangeイベント発火します。
こういうのかっこいいしプロダクトで使おうよと言ったんですが、@SubaruG に大反対されて悔しいのでブログに載せておきます!!!!!!!!!!!
使っていいよ!!!!
HaXeで関数型っぽくクイックソートするとこうなった
using Lambda; したかっただけとも言える。
switchが値を返す。switch文の中では一番最後に評価された値が変える。
Lambda使うと各種アルゴリズムを適用しやすいように二分木なList型に変換して、List#array()を呼ぶと普通の配列で返すっぽい。
HaXe触ってみた
適当に書いた奴をGithubにあげておいた。
mizchi/haxe-try https://github.com/mizchi/haxe-try
enchant.jsもHaXeから触ってみた。nodeとenchant.js、enchantの型アノテーションは
haXeでenchant.jsする利点 - mitamex4uの日記 http://d.hatena.ne.jp/mitamex4u/20111230/1325248577
を使わせていただきました。
とりあえずクイックソート
配列操作系のユーティリティはあんまりなさそう。
undescore.jsクローンのUndescore.hxってのはあった。が、名前空間がほんとにUnderscore.mapとかで冗長な気が…
HaXeを使う指針
何よりも気に入ったのが型がオプショナルであるという点。Dynamic型にすればそれ以下のプロパティに対して型チェックを行わない。
JSから触る場合、元が型がない言語なので、絶対な型安全はありえない以上ある程度ラップして妥協することになる。
と同時に、それ以外のロジックはHaXeのピュアなロジックで吸収する。型安全な世界は絶対に崩れないように書くことで堅牢なコードにすることが可能。
外部に依存しないようなコードは、他の言語にもコンパイルが可能なのでコードの再利用性は高い。(つってもやっぱ独特なコードなんだが…
HaskellがIOモナドで純粋でないものを切り離すように、安全な部分と危険でない部分を分離する方向で、JSをラップして、できるだけ純粋なHaXeのロジックで、という指針がよさそう。
実際にコーディングする際、ライブラリのコードを触る部分より、自分で書いたほうが多くなるとは思うので、必要な物だけtypedefなりして型アノテーションをつけるといい。補完がきくようになるし。
HaXeの駄目な点
Coffee書き慣れた身からすると記述はやっぱり冗長。高階関数のためにLambdaはなにかしらの略記がほしい。
Function型がない?。コールバックの記述に不安が残る。サンプルコード見てもDynamic型を渡していた。Int -> Void みたいな書き方はできるみたいだが。
コンパイルされたコードはやっぱり読みづらい。
HaXeとSublimeText2
補完はそこそこ効く。そもそもIDEあんまりすきじゃないのでこれぐらいで十分。
InteliJとMonoDevelop版もあるらしいので、本格的なのはそっちに期待しておく。
HTML5でロードオブナイツとかいうゲームを作った
陣取り戦争ゲーで、iPhoneのUnity版からの移植です。
ロードオブナイツ - Yahoo!モバゲー http://yahoo-mbga.jp/game/12011436/detail
PC(Chrome/Firefox/IE9以上)とスマホ(Webkit)で同じ物が動きます。ヤバゲーとモバゲーでアカウントは共有してるので、どっちからでも入れます。
HTML版としては既存コードとか一切なかったんで、JSに関してはフルスクラッチです。というか全部CoffeeScriptです。
ある日会社で楽しくHaskellでKPI計算モジュール書いてたら、
「UnityのネイティブのゲームをHTML5に移植したい」
みたいな話が降ってきたので、移植しました。3ヶ月ぐらいで。
プラットフォームはモバゲー(スマホ) + ヤバゲーです。HTML5なのは、スマホとブラウザゲーで開発リソースを共通化したかったから。
ヤバゲーのゲーム、ほとんどFlashで、JSでゴリゴリ動いてるのは割と異彩はなっている感じがありましたね。作ってから気づいた。
個人的なテーマ
一応趣味でHTML5っぽいゲーム作ってた身としては、Unityで書かれた本家を再現することを目標としていて
クオータービューのマップとか実装したんですが結構うまくやれた気がしてます。
もちろん僕一人で作ったわけじゃないですが、はじめて大人数開発を経験して、しかも技術的に超エッジなことをやらせてくれたので、作っててすごく楽しかったですね。Android標準ブラウザへの殺意を除けば。
分量
HTML 3000行
CSS(SCSS) 8000行
CoffeeScript 21000行
サーバーに関しては、元あるものを使ったので、基本クライアントコードの実装でした。
開発時はRailsのsprocketに頼りまくりました。デプロイされるときはindex.htmlとall.cssとall.jsだけ吐いて、Railsに依存しない謎構成でした。
Railsはコンパイラ。これについてはあとで何かしらの連載が上司のブログで展開されるんじゃないかなー(チラッ
テスト環境
nodeで無理矢理読み込んでJSDOM使ってテスト。
ってのはピュアJSのロジックが多かったからです。
nodeのライブラリで使ったのはこんな感じ。
- mocha
- jsdom
- should
- sinon
JSなんでほとんど対象はビューになるんですが、テストコードは6000行ぐらいあります。足りないので足さねば。
これからの実装
PC版専用ビューつくるらしいですね(って書いてある
オマケ