I'm using Netwire to write a program that is driven by events from the network. I guess there are three questions here:
What makes
Control.Wire.Unsafe.Event
unsafe? Like the title says, what invariants do I need to maintain to use it safely?I decided I need something like this:
mapMaybeE :: Monad m => (a -> Maybe b) -> Wire s e m (Event a) (Event b)
. The context is I have messages coming in from the network and I want to respond to only some of them. This is what I wrote:mapMaybeE :: Monad m => (a -> Maybe b) -> Wire s e m (Event a) (Event b) mapMaybeE f = arr go . arr (fmap f) where go WU.NoEvent = WU.NoEvent go (WU.Event Nothing) = WU.NoEvent go (WU.Event (Just a)) = WU.Event a
Is that "legal"? Or am I supposed to inhibit if there's no event?
Does Netwire make sense for this sort of problem? All the examples I've seen are of games that loop continuously. Here, I only want to step the wires when there's something to be done. Mostly, that will be network events, but I might also want to do things on a timer. E.g. an event comes in, then five seconds later the program does something. It shouldn't have to loop continuously until the time in the session is five seconds greater than when the event came in.
For a lot of these answers, "correct" or "legal" depends on what you want your application to do. "Idiomatic" might be a more interesting question, but as the library author has passed away, it's difficult to answer these questions definitively. The following therefore only represents my experience and may not be correct:
The "unsafe" part of
Control.Wire.Unsafe.Event
is the idea that you will be working with discrete instances in time, and you may not necessarily preserve the continuous time semantics that your program expects. In particular, there's no difference (from a type perspective) between an event happening in a simulation with a time state (thes
inWire s e m a b
) that's represented as anInteger
vs aFloat
, so you have to be careful to make sure that what you're doing makes sense for your application. The included general purpose combinators don't have that risk in the sense that they work with any sensible definition of "time". From the documentation ofdata Event
:From the README:
There's certainly nothing "illegal" about doing it that way. When you inhibit is totally dependent on your application. The key difference being that if you compose wires that inhibit, the inhibition "bubbles up" to the first wire that handles it (such as with an
Alternative
:(<|>)
). ProducingNoEvent
is fine if there's no event. :) The behavior you're looking for might be better modeled using the existing combinatorsdropWhileE
andfmap
fromControl.Wire.Event
though:Yes,
netwire
makes sense for any problem where you have to simulate the state of a system that has time-dependent semantics. To expand,Something is going to need to keep track of this timer, so you're not going to get around having a loop in one capacity or another. (Maybe you can have the operating system do it via a call to
sleep
or something, but internally there's still some loop somewhere...)netwire
lets you model the behavior of your system explicitly and respond to these kinds of events: both network events and timer events. Because Haskell is lazy, if you compose wires such that "really complicated wire" depends on a timer, the result from "really complicated wire" will not be evaluated until the timer expires (seeafter
.)