メインコンテンツまでスキップ

PureScriptのAffの作り方

· 約3分

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