2.0.7以降のtuaregでインデントがうへぇ、となったとき

最近、久しぶりにEmacsの環境を再構築しましたが、その際に、色々なelispも一緒に最新になったのですが、いくつか動作が変わったりとかいうのがありました。

その中で一番びっくりしたのが、tuaregが最新になったのを忘れて.mlファイルを開いたときです。まず何が違うって、

  • faceがかなり変わっている
  • インデントがびっくりするくらい変わっている

特に2番目が深刻で、どうやっても直ってくれずに途方に暮れていましたが、よくソースを見たら、一つ変数の値を設定するだけでなんとかなることがわかりました。

(setq tuareg-use-smie nil)

これで今迄通りのインデントになりました。

smie?

SMIEは、Simple Minded Indentation Engineの略で、インデントのための様々なヘルパーやエンジンを提供しているものです。24.3から新しく提供されたらしいです。
詳しくはこちらで。
Emacs の Major Mode におけるインデント計算を楽にする smie.el

tuaregのソースを見たときに、この部分に関連するソースに大量のTODOが貼ってあったので、とりあえず使ってバグを出してくれー、といったところでしょうか。
本当はこっちを利用するようにした方がよさそうなので、機を見てまた有効にしようとは思います。

最近のJavaScriptの開発環境(On 仕事)

書く気力がある内に、少しでもブログを書いた方がいいなー、と思い、どれくらいぶりか忘れましたが、二日連続での更新です。

ここ二ヶ月ほど、仕事の都合ではありますが、かなり自由のきく状態で、ひたすらにJavaScriptの開発をやっていました。結構分量が多い開発でしたが、その過程で、今やっておかないともう無理や!と思い、JavaScriptの開発環境を整えました。

その前に

今のプロジェクトに約10ヶ月ほど参加していますが、その間にも少しずつですが、環境の改善を進めていってはいました。変遷は大体こんな感じです。

  • 最初
    • 約5万行〜のJavaScript。ほとんどがグローバルにぶちまけられている & jspに「直接includeされている」ものが大半
    • デバッグがものすごい大変。別の.jsに書いてあるグローバル変数とか関数を使っていたりして、非常に読み解きづらい。というか無理。
    • セミコロンすら無いJavaScriptが大半。
  • 去年の10月頃まで
    • どうしようもなくなってきたので、少し時間を使って、namespaceとモジュール化。
    • ただ、既存があまりにもスパゲッティすぎて、これも付け焼刃にしかならなかった。
  • 去年の9月〜11月
    • Backbone.jsを真似した独自ライブラリを作成
      • ViewとModelとCollectionのみ
      • なぜかというと、jQuery1.4.4だから・・・
    • 一部の画面をこのライブラリで書き直してみたりして、多少手応えを得る。
    • このタイミングで、JSDeferredとunderscore.jsも導入。

今回の話は、ここから先の話になります。

今のJavaScript開発環境

ひとまず、色々とどうしようもなかったので、今回自分が弄った画面は、客先に「作り直しますんでいいっすか?」「(間に合うなら)いいよ」と言う会話を経て、本当にゼロから作り直しました。

作り直す過程で、Request.jsを使う、本物のBackbone.jsを使う、ということを決定しました。Backbone.jsについては、jQuery1.4.4でもイベント周りだけをなんとかすれば動作するということがわかったので、そこだけ自分で修正したものを利用してます。
それ以外にも、この際なので(ぉぃ)色々と導入することにしました

  • JST
    • テンプレートを管理するために導入。今のところ、underscore.jsのtemplateをMastache様に使えるようにしたもので必要十分です。
  • Sass
    • 今回の画面はかなり色々やらかさないとならなかったので、この際入れたれー、と導入しました。後悔はしていません。
  • YUIDoc
    • そもそもDocコメントすら無い部分が多過ぎたので、今回導入。今のプロジェクト形式では、JSDocよりもこっちの方が書いてて楽でした。

色々導入したのと、コンパイル系のツールが増えたので、Gruntを利用することにしました。それまでも、前までいた人がGruntでjshintをできるようにしてくれていたので、このGruntfileを十分に活用することにしました。今回で導入したプラグインは以下の通りです。

  • grunt-contrib-jst
  • grunt-contrib-sass
  • grunt-requirejs
    • grunt-contrib-requirejsも試しましたが、どうにもしっくりこなかったため、こっちにしました
  • grunt-parallel
    • jst/sass/requirejsをそれぞれコンパイルしないとならないので、それぞれ並列で動作するようにしました、が、PCの性能の問題でぶっちゃけ性能の向上が体感できません・・・

本当は、PCの性能が許すのであれば、Vagrantとかを導入して、ローカルを汚さずに*nix系の開発環境を構築するー、とかも考えたのですが、4GBのメモリだと、開発環境を起動しているだけで3.6GBくらい常時利用している状態なので、とてもじゃないけど無理でした。

まとめると、

  • Backbone.jsを導入して、クライアントサイドMVCを推進
  • Require.jsを導入して、モジュール化を推進
  • JSTを導入して、画面とロジックのさらなる分離
  • Sassを導入して、煩雑になったCSSを(多少なりとも)管理できるように
  • JSDeferredを導入して、非同期処理を同期的に管理できるように

というのをこの二ヶ月ほどかけてやりました。最後のは前からやっていましたけど。

やった甲斐はとりあえずあって、バグの修正とかの範囲がとても狭くなりました。前だと、どのグローバル関数が悪さをしているのかよくわからないとかほんとあったので・・・。

現状の課題

もちろん、完全な環境とかはないので、今も課題は残っています。ただ、将来の課題よりも残っている課題の方がやばいです。

今回Require.jsを導入して、ある画面はラクチンになったのですが、既存の画面にがっつり影響が出てしまいました。影響の原因はだいたいわかっていて、Require.js特有の読み込み順序の問題です。

利用するRequire.jsのファイルは、すべてコンパイルしたものを利用するようにしています。開発時も同様の形式としてしまっています。単純に時間がなかったので。なのですが、既存で利用しているjQueryと、Require.jsで利用しているjQueryが干渉してしまい、上手く動作しない、というのが原因でした。
ただ、プロジェクトで作成していた独自ライブラリにも原因はあって、非常に理解に苦しむのですが、jQueryオブジェクトの中にネームスペースを切っているのも大きいです。

この解決として、既存で読み込んでいたJavaScriptファイルを、Require.jsで読み込んだファイルの前後で再度読み込む、という暴挙に出ました。商用サイトだとぶっ殺される気がしますが、幸い社内サイトなのでまだ許容範囲です。無論、全体を速やかにRequire.jsの内部に取り込んでやる予定です。

まとまってる?

現状の開発環境を作る上で、多数のサイトを参考にしました。うろ覚えのサイトも多いのですが、幸いいくつかEvernoteに残っていたので、いくつかリンクを記載しておきます。

各ライブラリの本家。わかんなくなったら参照してます。

既存のjQueryとRequriejs上のjQueryを共存させる時に参考にしました

YUIDoc & Gruntの導入について参考にしました。
【YUIDoc】のドキュメントを自動生成する。

JSTの導入はここをほぼそのままパク・・・げふんげふん、参考にさせて頂きました。
grunt-contrib-jstでjsテンプレートを管理する。

Require.jsの導入について参考にさせて頂きました。
RequireJSをプロジェクトで使ってみての所感

最新はよくわかりませんが、一先ずモダンなJavaScript開発環境を作ることはできました。が、今度はこれをプロジェクトメンバーが使えるようにしないとならないという難題が待っています。こっちの方が頭使うかも・・・。

Go言語始めました。

気付いたら今年も始まってからすでに一ヶ月が経過しようとしています。時間の流れの速さまじパネェっす。

仕事では、この一ヶ月弱ほぼJavaScriptを書き続けていたような感じですが、新しいこともやりたいなー、ということで、最近トレンド急上昇中のGo言語に入門してみました。

とりあえずTour of Goはひととおりやりました。最後のGo routineがよくわからないのですがっ・・・。
http://go-tour-jp.appspot.com

何でもいいので、仕事でも使えそうなツール類を作ってみようかと思ってます。まだ触りたてなので、言語とかに関する感想は控えますが、標準のツールの充実っぷりが、他のコンパイル言語と比較してかなり飛び抜けているので、C言語とかにくらべればよっぽどとっつきやすいです。触りやすいという意味で。

去年も結局仕事とゲームに追われてしまい、なんだかんだ出来ない状態でしたが、今年はAndroid(まだ大丈夫だよね?)とかそっちの方面にも少しは手を出してみたいです。一応年始なので抱負ということで。

あ、後はもうちょっとブログを更新しないと・・・。

emacsでパーサーコンビネータみたいなものを作成してみた

年末から年始にかけて、忙しくなりそうでちょっとガクブルです。でも3年くらい前は今の5倍くらい残業してたのかと思うと、人間以外となんとかなるもんなんですね。望んでやりたかーないですが。

閑話休題

Emacsでは、文字列の検索は基本的に正規表現を利用して行っていますが、これをガリガリと書いたあとで見返すと、???的な感じによくなります。これはどんな言語の正規表現でも一緒だと思いますし、そう考える方々は、Perl5互換の正規表現を利用できるようにしたり、色々な解決手段が模索されているようです。

ちょっと書捨てする場合とか、そのときだけ検索したいものとかについては、その場で書けばそれでいいと思いますが、Emacs Lispでちゃんとしたスクリプトを書こうとすると、これをちゃんと管理していく必要があります。
ぶっちゃけこれがめんどくさいです。また、規模が大きくなればなるほど、これの管理がどんどん厳しくなっていきます(多分)

Haskell/OCamlとかの関数型言語では、これの解決手段として、パーサーコンビネータが選ばれているようです。Parsecが代表格でしょうか。

じゃあelispでもやってみよう!ということで、あまり知らないのですが作り始めてみました。

現状

で、とりあえずlexerの作成とcombinatorの作成くらいはできるようになった気がする、というところです。

https://github.com/derui/eparse

開発はgithubでやっています。よほどじゃないかぎり、最近はgithubリポジトリを作ってしまった方が、MacBookでもデスクトップでも作業できるのでイイ!ですね。
ちなみにこのプロジェクトではOMakeを使ってテストとか実行できるようにしています。cloneしてから、

$ omake test

とすれば、ert-expectationsを利用したテストケースが実行されます。それと、24.3から追加された(はず)cl-libs.elというライブラリを利用しているので、24.3以外じゃ動作しません。あしからず。

絶賛開発中というか迷走中というか、という感じで、インターフェースすらよく固まっていません。とりあえず今はこんな感じに書けます。

(funcall (eplib:lex (let (ch)
              (setq ch (<- 'char ?a)))
              (setq ch (<- 'char ?s)))
              (success ch))
     (eplib:make-source "as")))

eplib:プレフィックスは基本的にライブラリなので、実際には利用されない感じ?になると思っています。'charというシンボルは、eplib:define-lexerというマクロで定義したlexerを呼んでいる感じになります。テストの中にわかりづらいですが色々と書いてあります。

作ってみて

elispで、このようなものを初めて作ってみました。macroとかをこれだけちゃんと書いたのも初めてです。lispのmacroは強力だ、とよく聞きますが、今回それを味わったような感じです。

しかし、elisp特有の問題というかなんというか、作っている間に色々と???となる部分も散見されました。

  • lexical bindingが上手くいかない
    • どうも「setqとかされた」変数だけがlexical bindingされるらしいです
  • 一時関数がものすごく使いづらい
    • cl-fletを使えばましになりますが。
  • macroがよくわからん
    • 特にシンボルが絡むともう。。。

いくつかについては、経験を積めば大分わかるようになるとも思いますが。ただlexical-bindingだけがどうにもよくわからないっす。

TODO

今の課題としては、半端ないくらいに遅いです。どれくらい遅いかというと、

  • aという文字を1000文字連結
  • できるだけ長い文字列を取得する(正規表現だと a* )

という条件で、benchmarkを行うと、だいたい2000倍くらいの差があります。2000倍て・・・。ちなみに、このlexerは前から順に評価していくので、10000文字とかあっても、最初の何文字かだけしか文字が無いのであれば、正規表現よりも速くなったりします。

これをなんとかしたいんですが、defsubstとかでインラインにしたくても、defsubstを使ってみるとターミナルの文字が化けてしまったりして、テストが動作しません・・・。他にも高速化の手法はあると思うんですが、内部で結構いろいろなことをしているので、その辺を整理する必要もありそうです。

なんにせよ、作ってみないとパーサーコンビネータとかもよくわかりませんね。それっぽく動作するようにはしてみましたが、まだまだ先は長いです。

WebWorkersをBackbone.jsで使ってみたい

なんかすごい久しぶりにブログの更新をする気がします。

最近はまた忙しくなりそうな雰囲気がしてきていますが、それでも現場ではかなり好き勝手にさせてもらってるので、実際は結構楽しいです。

そんな現場ですが、今のところ最大の問題は、数万行〜10万行クラスの「グローバルをフルに使った」JavaScriptとの戦いです。ぶっちゃけかなりきついのと、これからの機能拡張が続くことを考えたら、これ以上ひどくなる前になんとかすべきだ、ということで、クライアントサイドMVCの導入を検討することにしました。本当はAltJSまで導入したかったのですが、引き継ぎとかスキルレベルとか、あとは地味に現場のPCのスペックが厳しいので、それはさすがに見送りました。

しました、となっていますが、実際には一部導入していて、どんどん進めていっている状態です。今のところクライアントサイドMVCはかなりの種類が出ていますが、実際大勢としてはEmber、AngularJS、Backbone.jsあたりになると思います。
ただ、現場のJavaScriptは、悲しいかなjQuery 1.4.4というかなり古いもので、バージョンアップはしばらく望めないのです。Backbone.jsとかは1.7以上じゃないと動作しないし、他も何をいわんや、というところでした。

そこで、ソースが短いと評判のBackbone.jsのインターフェースに合わせる形で、jQuery 1.4.4の範囲で動作するようなライブラリを作って、それでMVCをやっています。ほとんど合わせているので、もしもBackbone.jsを本当に導入したとしても、あまりソースを弄らなくても大丈夫なはず、という感じにしています。

WebWorkers

さて、結構前から気にはなっていたのですが、どうにも縁が無かったWebWorkersですが、今回使ってみることにしました。といっても、数値計算とかするようなアプリじゃないので、普通にAjaxをバックエンドで行うような想定です。

実際には、これをBackbone.jsとかで使ってみたいのですが、WebWorkersの仕様上、かなりそれは難しい模様・・・。やはり、JavaScriptファイルを指定しなければならない & jQuery使用不可、というのは結構厳しいです。$.ajax使えませんし。

それならばと、これも最近導入したJSDeferredを利用して、結構それっぽくAjaxを書けるようにもしてみましたが、JavaScriptファイルを指定しなければならない、というのはかなり厳しい感じですね。
ただ、http://blog.livedoor.jp/dankogai/archives/51504379.htmlこんな記事を見付けたので、3年前の記事ではありますが、仕様自体はほとんど変わっていない(と思う)ので、これを利用してもっと活用できる形にしたいと思います。

とりとめも無い上に中身も無い話でしたが、ひとまずこれで。

Scala + android-pluginを試してみた。

少々思うことがありまして、Androidで一つツールを作成してみようと思い、こちらのページを参考にして環境を作成してみました。

http://taisukeoe.github.io/blog/2013/06/08/scala-android-intellij/

私はGentooなので、事前に色々とやっていますが、とりあえずgiter8で取得したテンプレートを実機に転送してみました。

・・・が、aaptが無い、とか言われてコンパイルが通ってくれません。どうやら原因は、SDK Platform Toolsが18.1に上げたときに、platform-tools/にaaptとかdxとかが無い、という状態になっていたからのようです。
これはどうしようもないので、とりあえずシンボリックリンクを貼っておきました。

$ ln -s ../build-tools/18.1.0/aapt aapt
$ ln -s ../build-tools/18.1.0/dx dx

これで動作するようになりました。

しかし、初めてAndroidアプリをやってみていますが、これはEmacsだと正直辛い・・・。Ensimeが上手く動作したとしても、レイアウトがXMLとにらめっこなのが辛いです。
https://github.com/pocorall/scaloid?title=Scaloidというのもあるようで、こっちだとlayout.xmlは書かなくても大丈夫、ということのようですが、とりあえず普通に作ってみたいので、なんとかこのままやっていこうかと思います。

そういえば

はてなへの投稿をsimple-hatenaからhatena-diaryに切り替えてみました。かなり快適です。

Vagrantを使ってElixirをシンプルに導入してみる

新しい言語を使ってみるときって、環境が汚れるのが気になりますよね。ならない?なるってことで・・・。

私はにわかGentoo使いで、わりと環境にはなんでも突っ込んでみる質なのですが、それで導入して使わなくなったものが(多分)やたらあります。ツールとかもしかり。
言語の場合、学習が終わったらしばらく使わない、とかも普通にありますし、単純に興味だけで触ってみたい、ということもあります。

そういう時に便利なのがVagrantだろう、ということで、Gentoo環境でVagrantの導入 & BoxにElixirの導入、までをやってみました。Vagrantのもっとイカした使いかたとかは他の方の記事を読めばいくらでもありそうなので、ここでは割愛です。

Vagrantの導入

GentooでのVagrantの導入は、いつものとおりemergeからインストールです。

sudo emerge vagrant

ただ、結構な数について、package.accept_keywordsに追加しないとならないと思います。しなくてもいい人もいるかもしれません。

話は変わりますが、以前から、/etc/portage/package.*を編集することに対して、なんかいい方法は無いもんか、と思っていましたが、flaggieというものがそれをやってくれるそうです。使ってみましたが結構楽になります。よろしければ是非。

話は戻って、vagrantの導入ですが、私はffi-1.1.5がどうしても入ってくれませんでした・・・。rubygems/builderが読み取れない、ということでしたが、gemsの場所が悪かったんでしょうか。しばらくrubyやってないんでよくわからなくなってきましたが・・・。とりあえずffi-1.4.0を導入して事なきを得ました。

さて、導入できたら、Vagrant公式ページがいいかんじのチュートリアルになってますので、http://docs.vagrantup.com/v2/getting-started から、基本を学びます。私もついでに学びます。
ここでは特にこだわりは無いので、precise32をそのまま使います。VirtualBoxは事前に入れておかないとこの先進めませんので、入れていない方は是非どうぞ。カーネルの再コンパイルが必要になる場合があります。

mkdir -p vagrant/elixir
vagrant init
vagrant box add precise32 http://files.vagrantup.com/precise32.box
vagrant up 

事前にvboxusersグループに自身を追加して〜などやっておく必要があります。あとVBoxManageへのパス通しも。VBoxManageについては、vagrantもアドバイスをくれるので、わかりやすいです。

Provisioningの設定

Vagrantでは、起動時に自動的にProvisioningしてくれる機能がありますので、これを利用します。Chefとかを利用することもできるようですが、そこまで大掛りである必要は無いので、そのままshellを使います。

ここまでで、Vagrantfileは次のようになります。

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Every Vagrant virtual environment requires a box to build off of.
  config.vm.box = "precise32"
  config.vm.provision :shell, :path => "bootstrap.sh"
end

bootstrap.shが、provisioningするときに利用されるshellになります。今回はelixirを使いたいので、以下のような中身にしました。

#!/usr/bin/env bash
echo "deb http://binaries.erlang-solutions.com/debian raring contrib" >> /etc/apt/sources.list
wget -O - http://binaries.erlang-solutions.com/debian/erlang_solutions.asc | sudo apt-key add -

apt-get update
apt-get install make
apt-get install -y esl-erlang

mkdir work
cd work
wget https://github.com/elixir-lang/elixir/archive/v0.10.3.tar.gz
tar xf v0.10.3.tar.gz
cd elixir-0.10.3
make install

これでelixirが使えるようになりました。いくつか外部に依存していますが、これはしょうがないでしょう。

Vagrant便利

気にいらなかったり失敗したら、vagrant destroyすれば無かったことにできますし、またvagrant upすればすぐに立ち上がります。多少マシンスペックがないと自在に使えないのが玉に瑕ではありますが、環境構築の容易さは半端ないです。

ちなみにデフォルトの状態だと、/vagrantが、Vagrantfileを置いているディレクトリと同期したディレクトリになります。下手に消すと泣くので注意。

saharaとか他の便利なプラグインもあるようなので、使っていきたいと思います。が、rubygems/formatが見付からないとかエラーが出て・・・。