1

StateTトランスフォーマーを Haste で正しくコンパイルできなかったため、独自の実装を作成しました。setInterval私の状態モナド内でjavascriptを動かしたかったと思います。への ffi 呼び出しは次のとおりsetIntervalです。

jsInterval :: Int -> IO () -> IO Int
jsInterval = ffi "(function(t,f){window.setInterval(f,t);})"

mに渡された後に backの結果を取得する方法は考えられませんでしたjsInterval。ということで使ってみましたIORefs

interval :: Int -> StateT s IO () -> StateT s IO Int
interval i m = StateT $ \s -> do
    ref <- newIORef Nothing
    id_ <- jsInterval i $ do
                       (_, s') <- runStateT m s
                       writeIORef ref (Just s')
    s' <- readIORef ref
    return (id_, s')

元の状態を維持しているため、これは機能しませんでした。書き込みの前に読み取りが行われました。IORefそのため、が書き込まれるまでループでポーリングする関数を作成しましたが、これは永遠にハングアップしました。

interval :: Int -> StateT s IO () -> StateT s IO Int
interval i m = StateT $ \s -> do
    ref <- newIORef Nothing
    id_ <- jsInterval i $ do
                       (_, s') <- runStateT m s
                       writeIORef ref (Just s')
    s' <- go ref
    return (id_, s')
  where
    go ref = do
      s <- readIORef ref
      case s of
        Nothing -> go ref
        Just s' -> return s'

この機能を実装することは可能ですか?MonadEventforのインスタンスを書き込もうとしましStateTたが、それも失敗しました。

4

1 に答える 1

3

FFI'ed に渡す IO アクションjsIntervalは、単純な IO アクションです。を使用してそのアクションを実装するとrunStateT、少し 'local' を実行しているだけStateTです。囲んでいるコードとは無関係です。

これは、コールバックとモナド スタックの一般的な問題です。コールバック ( のIO() パラメータjsIntervalがコールバックであるという意味で) は、定義で選択された固定モナドを持ち、他の場所で使用している可能性のある他のモナド効果に一般化する方法がありません。

コールバックは、一般に、呼び出し元の関数が完了してその状態が破棄された後、異なるスレッドで一度に複数回を含め、いつでも呼び出すことができるため、これは一般的に解決するのが難しい問題であることがわかります。

実用的な答えは、あなたが試したように、単にIORef;を使用することです。IORef囲んでいるアクションで を作成し、コールバックにそれを変更させます。必要に応じて、スタイルでコールバックを記述することもできます。StateT状態を から抽出してIORefに渡すだけrunStateTです。あなたのコードはこれを行いません.sトップレベルからパラメータを参照しているだけです.次のようなIORefを使用する必要があります.

id_ <- jsInterval i $ do
                   current_s <- readIORef ref
                   (_, new_s) <- runStateT m current_s
                   writeIORef ref (new_s)

アクションに a に対処する方法をMaybe教える準備ができていない限り、実際には使用できません- に対処する必要があるため、おそらくタイプ?mMaybeNothingStateT (Maybe s) IO ()

コードの 2 番目のロジックの問題 (?) は、s返された byintervalがまだ変更されていないことです。setInterval コードは、javascript がアイドル ループに戻るまでトリガーされない可能性があります。

コールバックを渡す一般的な問題は、何年にもわたって数回議論されてきました。次を参照してください。

https://mail.haskell.org/pipermail/haskell-cafe/2007-July/028501.html http://andersk.mit.edu/haskell/monad-peel/ http://blog.sigfpe.com/2011 /10/quick-and-dirty-reinversion-of-control.html

于 2015-09-25T09:41:32.043 に答える