More fun with reactive-banana-automation
joeyh.name
joeyh.name
There are a bunch of (well named) functions and control flow. No boilerplate.
Since "inverterPowerChange" returns a Behaviour http://hackage.haskell.org/package/reactive-banana-1.2.0.0/d... which is supposed to be "inspectable" at any point in time, shouldn't PowerChange, PowerOff and PowerOn actually be PoweredState, PoweredOff and PoweredOn? The former sound more like event names. Or perhaps should "inverterPowerChange" return an Event instead? http://hackage.haskell.org/package/reactive-banana-1.2.0.0/d...
If the function in OP was returning an Event, it would be a sequence of instructions to the freezer: "turn on now", "turn off now" and so on. As a Behavior, it's more like a set of times when the fridge should be on or off.
The API for reactive-banana doesn't seem to have a "value changed" combinator that creates an Event from a Behaviour alone; one must sample the Behaviour with another Event.
(Other FRP libraries somewhat similar in philosophy like reflex—not the JS Reflex—do seem to have Behaviour-like things that can fire events on change: http://hackage.haskell.org/package/reflex-0.4.0.1/docs/Refle... Although reactive-banana seems simpler to understand.)
On the other hand, I think that's necessary for mutually recursive definitions of behaviors and events to reach a fixed point, which is quite a neat feature although not one I've needed yet.
Remark: Event is a Monoid or a Semigroup only when the contained value is either a Semigroup. The most useful combining ability comes from Behavior's Applicative instance.
Still, I will think about renaming PowerChange, perhaps it could be clearer.
Well, compare to
PowerChange inverterPowerChange(Sensors *sensors)
{
if (sensors->lowpower)
return PowerOff;
if (sensors->wifiusers > 0)
return PowerOn;
return sensors->fridgePowerChange;
}
in a more traditional language. Nobody would make a fuss about that code.To elaborate, lowpower etc. on this line are Behaviors, which emit values by time:
react <$> lowpower <*> fridgepowerchange <*> wifiusers
while lowpower, fridgepowerchange, wifiusers in the react function bind to the values that are emitted by the Behaviors. (Just by looking at the code we can infer that their types are Bool, (Num a, Ord a) => a, and PowerChange respectively.) react lowpower fridgepowerchange wifiusers
| lowpower = Just PowerOff
| wifiusers > 0 = Just PowerOn
| otherwise = fridgepowerchange
None of these are simply state variables!Although this might have confused GP, I think this is nice since we can associate the name 'lowpower' for its semantics (The low power signal) and not have to worry about "this lowpower is a Behavior while that lowpower is a Bool".
But the value of writing this kind of code in Haskell is that Haskell makes code that couples very abstract interfaces VERY easily. This is really hard to explain without some examples, so I'm going to give you an example.
Here, let me show you from real code. I'm going to tweak some endpoint names, but this is real code I'm going to ship next week: https://gist.github.com/KirinDave/9b4380376f9f748bcf0f8440de...
What's cool about this code is that I'm describing a "generic" radAPI service with a bunch of endpoints stripped out. Everything is parameterized around 3 generic ideas: "p", the Storage service. "Patchable", a strategy for applying patches to a storage service (exposing runPatch), and "CanValidate", a strategy for authenticating requests based on some sort of stored string, auth headers, and body contents.
This is even better than mocking, because instead of deeply coupling the structure of my code to my tests, I only couple the capabilities.
react lowpower fridgepowerchange wifiusers
| lowpower = Just PowerOff
| wifiusers > 0 = Just PowerOn
| otherwise = fridgepowerchange
The big deal with inverterPowerChange is this line: return $ react <$> lowpower <*> fridgepowerchange <*> wifiusers
It binds values emitted by the lowpower, fridgepowerchange, and wifiusers Behaviors, and wraps around it, returning a Behavior that itself emits values.