1

So I wrote my own implementation of StateT because I couldn't get transformers to compile correctly in Haste. I think wanted to get the javascript setInterval working inside my state monad. Here is the ffi call to setInterval.

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

I couldn't think of anyway to get the result of m back after it is passed to jsInterval. So I tried to use 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')

This didn't work because it kept the original state. The read happened before the write. So I wrote a function that would poll in a loop until the IORef was written but this just hung forever.

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'

Is it possible to implement this function? I tried writing an instance of MonadEvent for StateT but that was also unsuccessful.

Robin Green
  • 32,079
  • 16
  • 104
  • 187
DiegoNolan
  • 3,766
  • 1
  • 22
  • 26
  • I don't think it's possible. You can wait for `setinterval` to call its function _once_ with `MVar`s. But the next functions calls can not affect the state inside the `StateT`. – chi Sep 25 '15 at 09:11
  • Not related to the actual question at hand, which @drquicksilver answered admirably, but I want to point out that wrapping `setInterval` manually is unnecessary; you should use [setTimer](http://hackage.haskell.org/package/haste-compiler-0.5.1.3/docs/Haste.html#v:setTimer) instead. – valderman Sep 25 '15 at 23:00
  • @valderman I tried doing that but ran into a similar problem as I was unable to implement MonadEvent for StateT. – DiegoNolan Sep 26 '15 at 02:34

1 Answers1

3

The IO action you are passing to your FFI'ed jsInterval is just a plain IO action. If you implement that action using runStateT you are just running a little 'local' StateT. It's unrelated to the enclosing code.

This is a generic problem with callbacks and monad stacks - callbacks (in the sense that the IO() parameter to jsInterval is a callback) have a fixed monad chosen in their definition and they have no way to generalise to other monadic effects you might be using elsewhere.

Since callbacks - in general - can be called at any time, including multiple times at once, in different threads, after the calling function has completed and its state has been destroyed - you can see that this is hard problem to solve in general.

The pragmatic answer is, as you have tried, to just use an IORef; create the IORef in the enclosing action and let the callback modify it. You can still write the callback in StateT style if you wish - just extract the state from the IORef and pass it to runStateT. Your code doesn't do this, you are just referencing the parameter s from the top-level : you need to use the IORef, something like this:

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

You can't really use Maybe unless you are prepared to teach the action m how to cope with a Maybe - it need to deal with the Nothing, so perhaps you want it to have the type StateT (Maybe s) IO () ?

A second logic problem (?) with your code is that certainly the s returned by interval will not have been changed yet - the setInterval code can't possibly have been triggered until javascript goes back into its idle loop.

The general problem of passing callbacks has been discussed a few times over the years, see:

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

etc.

drquicksilver
  • 1,627
  • 9
  • 12