実例で学ぶ purescript ライブラリの開発

# purescript-enchantjs

本記事では PureScript の初学者を対象にして、PureScript ライブラリの開発手順を紹介します。
[purescript-enchantjs](https://github.com/algas/purescript-enchantjs) は PureScript を使ってゲームを作成するためのライブラリです。
https://github.com/algas/purescript-enchantjs
purescript-enchantjs は [PureScript](http://www.purescript.org/) による [Enchant.js](http://enchantjs.com/) のラッパーです。Enchant.js を呼び出しているだけなので、ほぼすべての機能は Enchant.js に依存します。PureScript の手習いのために作ってみました。bower のライブラリとして登録してあります。

## PureScript の依存ライブラリ

PureScript でライブラリ開発を行うには以下が必要です。

- [Haskell Platform](https://www.haskell.org/platform/): Haskell の実行環境
- [node.js](http://nodejs.org/): JavaScript の実行環境
- [npm](https://www.npmjs.org/): Node.js のパッケージマネージャ(サーバ側)
- [bower](http://bower.io/): Node.js のパッケージマネージャ(クライアント側)
- [grunt](http://gruntjs.com/): タスク実行ツール

purescript だけあれば PureScript で書かれたソースのビルドはできますが、ライブラリとして開発するためには JavaScript 開発のためのツールを使うことによって効率よく開発できます。
インストール手順は次項で説明します。

## purescript のインストール

Haskell のビルド環境がセットアップしてあるものとします。
[Haskell Platform](https://www.haskell.org/platform/) を入れておけば間違いないです。
下記のコマンドで purescript がインストールできます。
`cabal install purescript`
筆者の環境では purescript 0.5.6.2 を使っています。

## purescript-enchantjs の使い方

以下のコマンドで purescript-enchantjs を取得してあるものとします。  
`git clone https://github.com/algas/purescript-enchantjs.git && cd purescript-enchantjs`

1. node.js + npm のインストール  
[nodebrew](https://github.com/hokaccha/nodebrew) や [nvm](https://github.com/creationix/nvm) を使って node.js をインストールしてください。
npm は同時にインストールされるはずです。
インストールはそれぞれのサイトを参考にするとよいです。
2. bower のインストール  
[package.json](https://github.com/algas/purescript-enchantjs/blob/master/package.json) に書いた設定にしたがって npm を使って bower 関連のライブラリをインストールします。  
`npm install`
3. grunt のインストール  
[bower.json](https://github.com/algas/purescript-enchantjs/blob/master/bower.json) に書いた設定にしたがって bower を使って grunt をインストールします。  
`bower update`
4. ビルドの実行  
[Gruntfile.js](https://github.com/algas/purescript-enchantjs/blob/master/Gruntfile.js) に書かれたタスクを grunt が実行します。  
`grunt main`  
これで PureScript で書いたコードが JavaScript (js/index.js) に翻訳されます。
5. ライブラリおよび画像のダウンロード  
Enchant.js 本体とサンプルアプリで使っている画像を github から取得します。  
```wget -O js/enchant.js https://raw.githubusercontent.com/uei/enchant.js-builds/master/build/enchant.js```  
```wget https://raw.githubusercontent.com/wise9/enchant.js/master/images/chara1.png```
6. 描画確認  
Webブラウザで index.html を開きます。
キャラクターが画面の上部を左右に歩き回っていれば成功です。

## どうやって描画されるのか

index.html を見てみましょう。

```html:index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Enchant Example</title>
    <script src="js/enchant.js"></script>
  </head>
  <body style="margin:0; padding: 0;">
    <h1>Hello</h1>
    <script src="js/index.js"></script>
    <script>
      enchant();
      onload = PS.Main.main;
    </script>
  </body>
</html>
```

上記のように enchant.js と index.js を呼び出していることが分かります。
index.js は grunt によって PureScript からビルドされた成果物です。
`enchant();` で enchant インスタンスを生成しています。
`onload = PS.Main.main` で [src/Main.purs](https://github.com/algas/purescript-enchantjs/blob/master/src/Main.purs) をページ実行時に呼び出すように記述してあります。
Main.purs は [Enchant.js Live Sample](http://enchantjs.com/) の JavaScript を PureScript で書いたものになっています。
## purescript-enchantjs を支える技術

### FFI (Foreign Function Interface)

PureScript には [FFI](https://github.com/purescript/purescript/wiki/Language-Guide:-FFI) という機能があり、これを使うことで JavaScript で実装された関数を PureScript の関数として呼び出すことができます。
既存の JavaScript ライブラリの関数を利用したい場合に FFI で関数をラップすることで、再実装せずにそのライブラリを使えます。

### 開発に使ったライブラリ

* [grunt-init-purescript](https://github.com/purescript-contrib/grunt-init-purescript)  
PureScript ライブラリ開発のためのひな形を生成してくれるツールです。依存ライブラリの設定を自動生成してくれます。  
grunt-init-purescript で出力されたファイルを元にして package.json, bower.json, Gruntfile.js を書きます。
* [purescript-easy-ffi](https://github.com/pelotom/purescript-easy-ffi)  
purescript-enchantjs では Enchant.js をラップするために FFI による関数を大量に記述しています。 purescript-easy-ffi を使うことで記述量を削減することができます。

```hs:before-easy-ffi.purs
foreign import log
 "function log(string) {\
 \  return function () {\
 \    console.log(string);\
 \  };\
 \}"
 :: forall r. String -> Eff (console :: Unit | r) Unit
```

```hs:after-easy-ffi.purs
log :: forall r. String -> Eff (console :: Unit | r) Unit
log = unsafeForeignProcedure ["string", ""] "console.log(string);"
```

## 思ったこととか

* PureScript に限らず AltJS 全般に言えることですが JavaScript やその開発環境の知識がないと AltJS による開発できません。
* 本当はもっと複雑なゲームサンプルを自作しようしていたのですが、状態遷移を記述するのが面倒で今回の記事には間に合いませんでした。 PureScript 力が足りてないようです。
* purescript-enchantjs を使ってゲームを作ってみたいという奇特な方向けにライブラリを内包していない [repository](https://github.com/algas/purescript-enchantjs-example) も用意してあります。
* grunt-watch を使うと自動でビルドが走るようになるから楽かもしれない。

## 参考文献

* [PureScript Slide](http://slides.pab-tech.net/flt-purescript): PureScript を書き始めるキッカケになったスライド。日本語。
* [PureScript by Example](https://leanpub.com/purescript): PureScript 実践入門的。電子書籍。Web では無料で読めますが課金しましょう。
* [PureScript for Web Development](http://curtis.io/posts/purescript-for-web-development.html): PureScript で書いたコードをブラウザで参照する際に参考にした記事。
* [Enchant.js Reference](http://wise9.github.io/enchant.js/doc/core/ja/index.html): Enchant.js 本家のリファレンス。