JavaScriptの関数をPureScriptから呼ぶ
定数 Math.PI を呼ぶ
まずは一番簡単なJSの定数から呼んでみます。例としてMath.PIを取り上げます。
同じディレクトリに以下のPureScriptのファイルとJavaScriptのファイルを作成します。
Math.purs:
module Math where
foreign import PI :: Number
Math.js:
'use strict';
exports.PI = Math.PI;
1引数の関数を呼ぶ
次は1引数の関数です。Math.absです。
Math.purs:
foreign import abs :: Number -> Number;
Maht.js:
exports.abs = function(x) {
return Math.abs(x);
};
2引数の関数を呼ぶ
2引数の関数を呼んでみます。
注意点はPureScriptはカリー化されているけど、JavaScriptの関数はカリー化されてないところです。 PureScriptにはカリー化されてない関数をカリー化したり、その逆を行う関数が用意されているので、それを使います。
runFn2 :: forall a b c. Fn2 a b c -> a -> b -> c
ここでは2つの数字を単に足し算する自作のJS関数addをPureScriptから呼んでみます。
Math.purs:
import Data.Function.Uncurried (Fn2, runFn2)
foreign import addImpl :: Fn2 Number Number Number
add :: Number -> Number -> Number
add = runFn2 addImpl
Math.js:
exports.add = function(x, y) {
return x + y;
};
副作用のある関数を呼ぶ
副作用のある関数を呼ぶ場合も注意が必要です。
PureScriptのEffectをJSでどのようの表現すればよいのでしょうか?
以下のようにEffectはJSでは単なる関数で表現されます。
Effect Int
function() {
return 1;
}
これを踏まえて Math.random をFFIしてみます。
foreign import random :: Effect Number
exports.random = function() { // 戻り値をEffectで囲む
return Math.random();
}
引数が1つの副作用のある関数 console.log もFFIしてみます。
foreign import log :: String -> Effect Unit
exports.log = function(message) {
return () => { // 戻り値をEffectで囲む
console.log(message)
};
}
コールバック関数を持つ関数を呼ぶ
コールバック関数を持つ関数をFFIする場合は次の2つの注意が必要です。
コールバックはPureScript側で定義されるので、今までとは逆にPureScriptの関数をJSから呼ぶ必要があります。
コールバックはたいてい副作用があるのでJS側で副作用を実行する必要があります。
ここでは ボタンをクリックしたときに呼ばれる onClick を例にFFIします。
コールバック関数をJS側から呼び出したときに
callback(event)()
のように最後に副作用を実行する必要があります。
foreign import onClick :: (ClickEvent -> Effect Unit) -> Button -> (Effect Unit)
exports.onClick = function(callback, button) {
return () => { // 戻り値をEffectで囲む
button.onClick((event) => {
callback(event)(); // Effect Unit を実行
});
};
};
2つの引数を持つコールバック関数を持つ関数を呼ぶ
2つの引数を持つコールバック関数の場合は、PureScript側でカリー化されている関数をJS側で普通に呼べるように2つの引数を持つ関数に変換する点に注意が必要です。
変換するには次の関数を使います。
mkFn2 :: forall a b c. (a -> b -> c) -> Fn2 a b c
ここでは forEach を例にFFIします。
foreign import forEachImpl :: forall a. Fn2 (Fn2 a Int Unit) (Array a) Unit
forEach :: forall a. (a -> Int -> Unit) -> Array a -> Unit
forEach f xs = runFn2 forEachImpl (mkFn2 f) xs
exports.forEach = function(callback, xs) {
return xs.forEach((x, i) => {
callback(x, i);
});
};