haskellnetwire

Use of `periodic` in NetWire 5


Consider the following code:

-- this defines what our 'state' will be
data Direction = North | East | South | West deriving (Eq, Show, Enum)
data State = State Int Bool Direction deriving (Show)

initialState :: State
initialState = State 0 True North

-- a simple routine to change a state and count the number of
-- changes
nextState :: State -> State
nextState (State i _ West) = State (i+1) False South
nextState (State i _ North) = State (i+1) True East
nextState (State i b s) = State i b $ (if b then succ else pred) s

-- a wire with local state
stateWire :: Wire s () m a State
stateWire = stateWireFrom initialState
  where
    stateWireFrom s = mkSFN $ \_ -> (nextState s, stateWireFrom (nextState s))

-- let's run the wire!
main = testWire clockSession_ stateWire 

As you can imagine, testWire will run the wire as fast as it can and print the output to screen. But what if I want to run my wire every 2 seconds? Looking at the docs, periodic may be the solution:

-- Since periodic generates events, asSoonAs is used to 'unwrap' the Event
main = testWire clockSession_ (asSoonAs . periodic 2 . stateWire)

This almost works. The output seems to be static for about 2 seconds, but when it's updated, it's clear that the wire were running while the output was stopped. Maybe I should do the other way around?

-- Now, this does make more sense to me...
main = testWire clockSession_ (stateWire . periodic 2)

However, the end result is exactly like my first try. What am I missing here?

EDIT: See this answer for a (inferior) alternative to the accepted answer.


Solution

  • The problem seems to be that you treat your stateWire as if it was a continuous wire, but it really should be an event wire itself. Assuming I understood your intent correctly, it should probably be accumE (flip $ const nextState) initialState - see the event docs for accumE - then you can use it like this:

    stateWire . periodic 2 (the other way round would not work).

    The reason why your original version doesn't work is that periodic doesn't inhibit when there's no event, it instead just produces a NoEvent value. And since your stateWire just ignores its input, whether or not an event is produced doesn't make any difference to it when the periodic wire is in front, whereas putting the periodic wire in the back just means 'periodically catch a snapshot of the current state', which is also not what you want.

    Note: 'Front' and 'back' in the previous paragraph refer to the order of execution, not to the layout in source code which is in reverse if you use the . combinator.