より実用的なPureScriptのFFI（基礎編）

# はじめに

PureScriptはHaskellライクな純粋関数型AltJSです。
PureScriptにはHigher Kinded PolymorphismやExtensible effects、Rank N Typesなどの強力な型機能があり、JavaScriptをそのまま書くのに比べて劇的にプログラミングが楽になります。
しかしPureScriptはJavaScriptの関数を使うAltJSなので、どこかで既存のライブラリなどを呼び出す必要があります。
そして静的な型のないJavaScriptの関数をどのように純粋関数型の世界に持ってくるのかということに頭を悩まされることになります。

この記事では以下のことを説明します。

- PureScriptのFFIの基本
- FFIを書くのに便利なモジュールを紹介
- JavaScriptの関数が副作用を持つ場合のFFIの書き方の注意点
- JavaScriptの関数が副作用のある場合に、より簡単に型を付ける方法（拙作モジュールの紹介）

FFIはPureScriptを実際に使う上で最初の壁になると思われるので、少しでもその壁を越える手助けになれば幸いです。

本当はさらにpurescript-affモジュールを使って非同期モナドにする方法も紹介したかったのですが、ここまででかなり長くなってしまったので、purescript-affについては次の記事で紹介したいと思います。

# PureScriptのFFIの基本

まず、PureScriptの言語機能のFFIについて見直してみます。
PureScript 0.7でFFIの部分が大きく変更されたので、前にPureScriptをちょっと勉強したことあるという人ももう一度確認してみてください。

PureScript 0.7から **FFIの方式がインラインでおこなう方法から別ファイルに分離** になりました。
これは将来的にPureScriptがJavaScript以外のバックエンドを持つ場合に備えて、明示的なJavaScriptへの依存を減らすためらしいですが、JavaScript部分が別ファイルになっていたほうがエディタの入力補助などを受けられてよいと思います。

ではPureScriptのFFIの基本について、我々の聖典である『[PureScript by Example](https://leanpub.com/purescript/read#leanpub-auto-the-foreign-function-interface)』から具体例を引用してみましょう。

ここではJavaScriptの文字列をURIエンコードする`encodeURIComponent`という関数をPureScriptから使えるようにしてみます。

まずPureScript側は型だけを定義します。

```purescript
module Data.URI where

foreign import encodeURIComponent :: String -> String
```

JavaScript側は同名の関数をエクスポートします。
ここで`// module Data.URI`というコメントは意味を持つので注意してください。
ちゃんと同じモジュール名を指定しないといけません。

```javascript
"use strict";

// module Data.URI

exports.encodeURIComponent = encodeURIComponent;
```

試しにこの関数をREPLで動かしてみましょう。
bower.json はたとえばこんな感じになります。

```javascript
{
  "name": "ffi-example",
  "devDependencies": {
    "purescript-console": "^0.1.0"
  }
}
```

PureScript 0.7から今まで以上に色々モジュール分離がおこなわれ、psciで実行した結果を表示するためのpurescript-consoleまで標準では使えなくなってしまいました。
なのでpurescript-consoleをdevDependenciesに入れる必要があります（この機能くらいpsciに入れてくれればよいのでは、と思ってしまいますが…）。

ファイル構成は以下のようになります。

```
ffi-example
├── bower.json
└── src
    └── Data
        ├── URI.js
        └── URI.purs
```

これでREPLを起動すると、

```
$ bower i && pulp psci
```

以下ように動作を確認することができます。

```purescript
> Data.URI.encodeURIComponent "http://www.nicovideo.jp"
"http%3A%2F%2Fwww.nicovideo.jp"
```

これでPureScript上で文字列をURIエンコードすることができました。

これがPureScriptの言語機能のFFIになります。

## 余談 pulp について

余談ですが、最近PureScript界隈ではビルドツールにpulpを使うことが多くなりました。
gulpやgruntを使う場合と違い、ビルド用のタスクを書かなくてもPureScriptのコンパイルができるのに加えて、browserify機能やウォッチ機能も最初から付いており、とても楽に使うことができます。

またpulpは標準のPureScriptツールの機能も便利に使えるようになっています。
たとえば`pulp init`でプロジェクトを生成すればpurescript-consoleが依存に含まれているのですぐにREPLを起動することができます。
また上でREPLを起動したときのように`pulp psci`を使えば.psciファイルを生成して、プロジェクトのPureScriptを読み込んだ状態でpsciを起動してくれますし、
`pulp docs`ではpsc-docsに適切に引数を与えてドキュメントを生成してくれます。

たとえば上記のモジュールでは`pulp docs`だけで

    ## Module Data.URI

    #### `encodeURIComponent`

    ``` purescript
    encodeURIComponent :: String -> String
    ```

のようなドキュメントが生成されます。
ライブラリを自作したらついでにドキュメントも生成して公開するとよいでしょう。

最近pulpが標準的に使われるようになったのは上記のようにPureScriptを使う上で非常に便利というのもあると思いますが、
JavaScript界隈で個々のツールが多機能化する一方で、gulpやgruntなどのビルドツールを使わずに`npm run`だけでタスクを実行しようという流れがあるので、その影響もあると思います。
PureScriptでも汎用のビルドツールを使って面倒になるより、専用のpulpを使ったほうが楽でいいということなんでしょうね。

# FFIを書くのに便利なモジュールを紹介

## purescript-functions

次に標準ライブラリのpurescript-functionsの紹介をします。
purescript-functionsは多引数の関数を扱うためのモジュールで、FFIを書くときに役に立ちます。

まず基本的なことですが、PureScriptの世界の関数はすべてカリー化されています。つまり一引数の関数しか扱うことができません。
なのでPureScriptの言語標準機能だけでJavaScriptの複数の引数を持つ関数を使いたい場合はいちいちFFIを使ってJavaScriptの関数をラッピングする必要があります。

たとえば2引数のJavaScriptの関数をPureScriptで使うためには、以下のようにJavaScriptのラッパー関数を書く必要があります。

```javascript
exports._parseInt = function(s) {
  return function(r) {
    return parseInt(s, r);
  };
};
```

これをPureScriptで以下のように型付けすると、

```purescript
foreign import _parseInt :: String -> Int -> Int
```

やっと使えるようになるわけです。

```purescript
> _parseInt "ff" 16
255
```

しかし引数の渡し方が違うという理由で、部分適用したいわけでもないのに、いちいちカリー化するようなラッピング関数を記述するのは面倒です。
このために使うのがpurescript-functionsです。

purescript-functionsの`Fn2`という型を使うと`parseInt`に直接型を付けることができます。

```purescript
import Data.Function

foreign import parseInt :: Fn2 String Int Boolean
```

Fn2を使うとJavaScriptのコードは

```javascript
exports._parseInt = parseInt;
```

まで短くすることができます。

この関数は`runFn2`を使うと実行することができます。

```purescript
> runFn2 _parseInt "ff" 16
255
```

runFn2をいちいち使うのは面倒なので普通のPureScript関数でラッピングすることになるでしょう。

```purescript
parseInt :: String -> Int -> Int
parseInt s r = runFn2 _parseInt s r
```

このように最終的に使いやすいようにラッピングする必要はあるかもしれませんが、JavaScriptのレベルでやるのとPureScriptでやるのでは型や表現力の差で書きやすさに大きな違いがあると思います。

逆にPureScriptの関数を複数の引数を持つ関数として使いたい場合(たとえばJavaScriptのコールバックに渡したい場合など)は `mkFn` という関数を使うとPureScriptの関数を非カリー化(uncurrying)することができます。

たとえば以下の `divides` というPureScriptの関数を`mkFn2`を使って定義した場合、

```purescript
import Data.Function
import Math
import Prelude

divides :: Fn2 Number Number Boolean
divides = mkFn2 $ \n m -> m % n == 0.0
```

このコードをJavaScriptにコンパイルすると以下のような2引数の関数になります。

```javascript
var divides = function(n, m) {
  return $$Math["%"](m)(n) === 0.0;
};
```

このようにpurescript-functionsを使うとJavaScriptの記述量を減らすことができ、場合によってはコンパイルで生成されるJavaScriptの量も少なくすることができます。

## purescript-nullable

次に小さなモジュールですが、purescript-nullableを紹介します。

null値の扱いは様々な言語で問題になりますが、PureScriptでもJavaScriptのnullをうまく扱う必要があります。
関数型プログラミングではnullになるかもしれない値をOption型やMaybe型のようなもので表現することが一般的ですが、
PureScriptでもFFIによりMaybe型にラッピングすることが基本になると思います。
しかし、JavaScriptのnullに対して直接型付けする方法もあります。
それがこの節で紹介するpurescript-nullableモジュールの`Nullable`です。

ここではDOMの中からIDで検索しエレメントを取得することができるJavaScriptの`getElementById`関数にPureScriptの型を与えることを考えてみましょう。

DOMの型を全部定義すると大変なのでpurescript-domモジュールから借りてくることにしましょう。
注意すべきなのは`getElementById`は要素が見つからない場合nullを返す関数ということです。
なので返り値をただの`Element`にすると、要素が見つからなかった場合に実行時エラーが起きることになります。
よって最後に返される型をpurescript-nullableのNullableを使って`Nullable Element`にしてみます。
すると以下のような型付けが考えられます。

```purescript
import Control.Monad.Eff
import Data.Nullable
import DOM(DOM())
import DOM.Node.Types(Document(), Element())

foreign import getElementById :: forall eff. String -> Document -> Eff (dom :: DOM | eff) (Nullable Element)
```

JavaScript側は以下のようになります。

```javascript
exports.getElementById = function(id) {
  return function(document) {
    return function() {
      return document.getElementById(id);
    };
  };
};
```

JavaScript側では特に何もせずにgetElementByIdを普通に呼び出しているだけです。
つまりnullを返すかもしれないということがPureScriptの型で表現されているだけなのです。

しかし、この`Nullable Element`はそのまま使うことはできないので、
purescript-nullableのtoMaybe関数を使ってMaybe型に変換して使うことになります。

```purescript
toMaybe <$> getElementById "hoge" document // たとえば Just <div id="hoge">...</div> のような返り値になる
```

purescript-functionsの`Fn`と`runFn`もそうでしたが、このようにPureScriptで直接扱いづらいJavaScriptのコードは`foreign import`で外部型を定義し、
その中にデータを入れて、必要なときに`runXXX`や`toXXX`などの関数で取り出すという方法が有効です。

# JavaScriptの関数が副作用を持つ場合のFFIの書き方の注意点

purescript-functionsはたいへん便利なんですが、一つ注意点がありまして、**Eff関数は直接型付けできません** 。
実はPureScriptの普通の関数とEffの関数とではJavaScriptになったときの形がちょっと異なります。

上記のgetElementByIdのラッピング関数をもう一度見てみましょう。

```javascript
exports.getElementById = function(id) {
  return function(document) {
    return function() {
      return document.getElementById(id);
    };
  };
};
```

よく見ると最後の返り値を`function ()`でラッピングしていることがわかると思います。
getElementByIdは副作用を持ち、`Eff (dom :: DOM | eff) (Nullable Element)`の値を返すわけですが、
このように返り値がEffになる場合は、`function ()`でラッピングしなければならないのがPureScriptのルールです。
よってpurescript-functionsモジュールを使って直接型を付けることはできないということになります。

『PureScript by Example』でも「[10.15 Representing Side Effects](https://leanpub.com/purescript/read#leanpub-auto-representing-side-effects)」でそのことに触れられています。

JavaScriptの`Math.random`関数を使いたい場合、PureScriptでは

```purescript
foreign import random :: forall eff. Eff (random :: RANDOM | eff) Number
```

という型を付けて、JavaScriptでは`function()`でラッピングしています。

```javascript
exports.random = function() {
  return Math.random();
};
```

またPureScriptのEff関数をコールバックとしてJavaScriptに渡す場合でも問題が発生します。
PureScriptのEff関数はJavaScriptになったときに`function()`が外側に付いてしまっているので、動作がおかしいことになるのです。
この解決策もFFIのJavaScriptでおこなうことになります。

たとえばコールバックを引数にとるJavaScriptの関数`readFile`があったとして、これに対してPureScriptのEff関数`callback`をコールバックとして与えたいという場合を考えます。

PureScriptの型は以下のようになるでしょう。

```purescript
foreign import readFile :: forall eff. String -> (String -> Eff (fs :: FS | eff) Unit) -> Eff (fs :: FS | eff) Unit
```

これに対してJavaScript側は以下のようにコールバックの呼び出し方に工夫が必要になります。

```javascript
exports.readFile = function(filename) {
  return function(callback) {
    return function() {
      readFile(filename, function (data) {
        callback(data)();
      };
    };
  };
};
```

注目してほしいのは`callback(value)();`の部分で、最後の`()`はPureScriptのEff関数を呼び出しているわけです。

このあたりのEff関数の扱い方がFFIの注意点になります。

# JavaScriptの関数が副作用のある場合により簡単に型を付ける方法（拙作モジュールの紹介）

（以下の節はPureScriptの標準的なやり方ではないので、無視してもらってもかまいません）

前節でEff関数のFFIの注意点について述べましたが、実際問題として、JavaScript関数の多くが副作用を持つことになります。
またコールバック関数の多くも副作用を持つことになるでしょう。
これらに対していちいちJavaScriptのラッパー関数を書くのでは、個人的にちょっと不便ではないかと思いました。

そこで [hexx/purescript-eff-functions](https://github.com/hexx/purescript-eff-functions/) というモジュールを作ってみました。

このモジュールには`ImportEffFnX`と`ExportEffFnX`という二つの型があります。
前者が副作用のあるJavaScript関数に対して使う型で、後者がPureScriptのEff関数をコールバックとしてJavaScriptに渡す場合に使う型です。

`ImportEffFnX`を使うと上の`random`関数は以下のように書き換えることができます。

PureScript側が以下のようになり、

```purescript
foreign import _random :: forall eff. ImportEffFn0 (Eff (random :: RANDOM | eff) Number)

random :: forall eff. Eff (random :: RANDOM | eff) Number
random = runImportEffFn0 _random
```

JavaScript側は以下のようになります。

```javascript
exports._random = Math.random;
```

purescript-functionsモジュールを使ったときと同じように、副作用を持つJavaScriptの関数でも型を直接付けられるようになりました。

PureScriptのEff関数をJavaScriptにコールバックとして渡す場合はどうでしょうか。
今度は上記の`readFile`関数を`ExportEffFnX`を使って書き換えてみます。

すると、PureScript側が以下のようになり、

```purescript
type Callback eff = ExportEffFn1 String (Eff (fs :: FS | eff) Unit)
         
foreign import _readFile :: forall eff. ImportEffFn2 String (Callback eff) (Eff (fs :: FS | eff) Unit)

readFile :: forall eff. String -> (String -> Eff (fs :: FS | eff) Unit) -> Eff (fs :: FS | eff) Unit
readFile filename callback = runImportEffFn2 _readFile filename (mkExportEffFn1 callback)
```

JavaScript側は以下のようになります。

```javascript
exports._readFile = readFile;
```

基本的な動作はpurescript-functionsと同じで、run系は実行する関数になり、mk系は作る関数になります。
PureScript側の記述は多くなりましたが、JavaScript側はすっきりしました。

JavaScript側のFFIでがんばる方法の問題点は、Eff関数の仕様がわかりづらい点と、記述のミスが実行時のエラーになるという点です。
拙作のpurescript-eff-functionsモジュールを使うと、この点をpurescript-functionsを使った場合と同じように改善することができます。

## purescript-eff-functionsでJavaScriptのthisの問題を解決する

この他に、さらにもう一つ、どうしてもFFIを使わなければならない問題があります。
悪名高いJavaScriptのthisの問題です。

JavaScriptの関数中のthisは呼び出し方によって変わります。
オブジェクトのメソッドのように呼び出す場合thisはレシーバを指すようになりますが、関数として呼び出す場合はグローバルオブジェクトを指します。
メソッドとして呼び出されることが想定されているものを、関数として呼び出してしまうとthisがおかしいことになり、実行時エラーになってしまいます。

しかし、この呼びわけをPureScriptから指定するのは困難です。
purescript-functionを使うと、JavaScriptを関数として呼び出すことしかできないためです。
仕方ないので、これもFFIのJavaScriptでがんばることになります。

そこで拙作のpurescript-eff-functionsモジュールには`MethodEffFnX`という型を用意し、この問題を解決しています。

たとえば上記の`getElementById`関数ですが、実は`document`に対してメソッドして呼び出さないと機能しない関数です。
これを`MethodEffFnX`を使って書き換えてみましょう。

```purescript
type Document = { getElementById :: forall t eff. MethodEffFn1 t String (Eff (dom :: DOM | eff) (Nullable Element)) }

getElementById :: forall eff. String -> Document -> Eff (dom :: DOM | eff) (Nullable Element)
getElementById id document = runMethodEffFn1 document.getElementById document id
```

今回はJavaScript側の記述は必要ありません。
このようにPureScriptのレコード型を使って、JavaScriptのオブジェクトに直接型を付けるのもやりやすくなります。

実際にpurescript-eff-functionsの`runMethodEffFn1`がどうなっているかというと、JavaScriptの`call`を使っているだけなんですが。

```javascript
exports.runMethodEffFn1 = function (fn) {
  return function (t) {
    return function (a) {
      return function () {
        return fn.call(t, a);
      };
    };
  };
};
```

以上、拙作のpurescript-eff-functionsモジュールを使うと、FFIのJavaScriptの記述量がさらに減らせることが理解していただけたと思います。
冒頭に述べたようにJavaScriptで書くよりPureScriptで書くほうが記述能力が高く、強力な型のサポートが得られます。
なので、なるべく工夫してなるべくJavaScriptよりもPureScriptで書くようにしていきたいと考えています。

# まとめ

今回はPureScriptのFFIの基礎の説明をしました。
慣れるとわりと機械的にJavaScriptの関数に型を付けていけるようになると思います。
さらにpurescript-affを使うと、JavaScriptで多発することになる非同期処理でもPureScriptらしい高機能で統一されたインターフェースを与えることができるのですが、
そちらは次回紹介したいと思います。

（あー、来年ぐらいには仕事でPureScriptを使いたいなあ……）