Use of `periodic` in NetWire 5

93 views Asked by At

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.

2

There are 2 answers

3
Cubic On BEST ANSWER

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.

0
Rafael Ibraim On

As an alternative to the accepted answer, it's also possible to filter out the NoEvent, without changing the wire:

main = testWire clockSession_ (stateWire . when occurred . periodic 2)

In this case, the wire will change the state, inhibit for 2 seconds and then change it again.

The other (accepted) answer works different: the wire will change the state, then continue producing the same result for 2 seconds and then change it again.