I'm playing around with the netwire package trying to get a feel for FRP, and I have a quick question.
Starting with the following simple wires, I'm able to emit an event every 5 seconds (approx)
myWire :: (Monad m, HasTime t s) => Wire s () m a Float
myWire = timeF
myWire' :: (Monad m, HasTime t s) => Wire s () m a Int
myWire' = fmap round myWire
myEvent :: (Monad m, HasTime t s) => Wire s () m a (Event Int)
myEvent = periodic 5 . myWire'
This is pretty nice and straight forward, but what I want to do next is map each event produced to a wire, that I can then watch update. I have an accumulator function like the following:
eventList :: (Monad m, HasTime t s)
=> Wire s () m a (Event [Wire s () m a Int])
eventList = accumE go [] . myEvent
where go soFar x = f x : soFar
f x = for 10 . pure x --> pure 0
I then introduce a new wire that will inhibit until the eventList
starts triggering events, like so:
myList :: (Monad m, HasTime t s) => Wire s () m a [Wire s () m a Int]
myList = asSoonAs . eventList
So I've gone from events to a wire containing a list of wires. Finally, I introduce a wire to step each of these wires and produce a list of results:
myNums :: (Monad m, HasTime t s) => Wire s () m [Wire s () m a Int] [Int]
myNums = mkGen $ \dt wires -> do
stepped <- mapM (\w -> stepWire w dt $ Right undefined) wires
let alive = [ (r, w) | (Right r, w) <- stepped ]
return (Right (map fst alive), myNums)
myNumList :: (Monad m, HasTime t s) => Wire s () m a [Int]
myNumList = myNums . myList
And finally, I have my main routine to test it all out:
main = testWire clockSession_ myNumList
What I expect to see is a growing list, where each element in the list will show it's creation time for 10 seconds, after which the element will show a zero. What I'm getting instead is a growing list of static values. For example, what I expect to see after a few steps is
[0]
[5, 0]
[10, 5, 0]
[15, 10, 0, 0]
and so on. What I'm actually seeing is
[0]
[5, 0]
[10, 5, 0]
[15, 10, 5, 0]
So I know my accumulator function is working: every event created is being converted into a wire. But what I'm not seeing are these wires emitting different values over time. My statement for 10 . pure x --> pure 0
should be switching them over to emitting 0 after the time has elapsed.
I am still new to FRP, so I may be fundamentally misunderstanding something important about it (probably the case.)
The problem is that the wires generated from the events are not persistent. A given value of type Wire s e m a b
is actually an instance in time of a function that produces a value of type b
from a value of type a
. Since Haskell uses immutable values, in order to step wires, you have to do something with the resulting wire from stepWire
otherwise you get the same output for the same input. Take a look at the results from myList
:
Event 1: [for 10 . pure 0 --> pure 0]
Event 2: [for 10 . pure 5 --> pure 0, for 10 . pure 0 --> pure 0]
Event 3: [for 10 . pure 10 --> pure 0, for 10 . pure 5 --> pure 0, for 10 . pure 0 --> pure 0]
... etc
When you step each of these wires, you're only going to get [.., 10, 5, 0]
every time because you're reusing the original value of the for 10 . pure x --> pure 0
wire. Look at the signature for stepWire
:
stepWire :: Monad m => Wire s e m a b -> s -> Either e a -> m (Either e b, Wire s e m a b)
This means that for a statement such as
(result, w') <- stepWire w dt (Right underfined)
... the w'
should be used the next time you need to call stepWire
, because it is the behavior at the next instance in time. If you have a wire that produces wires, then you need to offload the produced wires somewhere so that they can be processed separately.
For a program that (I believe) gives you the behavior you want, please refer to this code.
$ ghc -o test test.hs
[1 of 1] Compiling Main ( test.hs, test.o )
Linking test ...
$ ./test
[0]
[5,0]
[10,5,0]
[15,10,0,0]
[20,15,0,0,0]
...