setTimeoutをAff化する
簡単な例としてJSのsetTimeoutをAff化します。
考え方としては、JSでsetTimeoutをPromise化するのとほぼ同じです。
注意点としては以下の2点くらいでしょうか。
- PureScriptではカリー化された関数を使いますが、JSではカリー化されてないので、その取扱に注意すること
- JS側でPureScriptのEffectの扱い方
PureScript側の実装
-- ネイティブ(JS)のsetTimeoutのインターフェース。
-- Fn2はカリー化されてない引数を2つ持つ関数を定義する。
-- Timeout.jsでJSの実装をする。
foreign import setTimeoutImpl :: Fn2 (Unit -> Effect Unit) Int (Effect Unit)
-- Fn2によって定義された関数をrunFn2でカリー化する。
setTimeout :: (Unit -> Effect Unit) -> Int -> Effect Unit
setTimeout = runFn2 setTimeoutImpl
-- setTimeoutをmakeAffによってAff化する。
-- makeAffはJSのnew Promiseのようなものと思って問題ない。
-- fはJSのPromiseのコンストラクタ内で使用するresolve, rejectのような関数。
-- fにはEither Error a型を渡すことになっていて成功時はRight、失敗時はErrorを渡す。
-- makeAffに与える関数の戻り値がEffect CancelerなのでmemptyでEffect Cancelerを生成している。
setTimeoutAff :: Int -> Aff Unit
setTimeoutAff ms = makeAff $ \f -> setTimeout (\_ -> f (Right unit)) ms *> mempty
JavaScriptの実装
exports.setTimeoutImpl = function(f, t) {
// PureScript側の戻り値がEffectの場合は関数で囲む必要がある。
return () => {
setTimeout(() => {
// f()はEffect Unitを返す関数なのでEffectを外してUnitを得るにはf()()とする。
f()();
}, t);
};
}
EffectをAffに変換する
AffはMonadEffectのインスタンスなので、liftEffectが使えます。
import Effect.Class (liftEffect)
liftEffect $ log "hoge hoge" :: Aff Unit