The Control.Arrow.Operations.ArrowCircuit
class is for:
An arrow type that can be used to interpret synchronous circuits.
I want to know what synchronous means here. I looked it up on Wikipedia, where they are speaking of digital electronics. My electronics is quite rusty, so here is the question: what is wrong (if anything is) with such an instance for the so-called asynchronous stream processors:
data StreamProcessor a b = Get (a -> StreamProcessor a b) |
Put b (StreamProcessor a b) |
Halt
instance Category StreamProcessor where
id = Get (\ x -> Put x id)
Put c bc . ab = Put c (bc . ab)
Get bbc . Put b ab = (bbc b) . ab
Get bbc . Get aab = Get $ \ a -> (Get bbc) . (aab a)
Get bbc . Halt = Halt
Halt . ab = Halt
instance Arrow StreamProcessor where
...
getThroughBlocks :: [a] -> StreamProcessor a b -> StreamProcessor a b
getThroughBlocks ~(a : input) (Get f) = getThroughBlocks input (f a)
getThroughBlocks _input putOrHalt = putOrHalt
getThroughSameArgBlocks :: a -> StreamProcessor a b -> StreamProcessor a b
getThroughSameArgBlocks = getThroughBlocks . repeat
instance ArrowLoop StreamProcessor where
loop Halt = Halt
loop (Put (c, d) bdcd') = Put c (loop bdcd')
loop (Get f) = Get $ \ b ->
let
Put (c, d) bdcd' = getThroughSameArgBlocks (b, d) (f (b, d))
in Put c (loop bdcd')
instance ArrowCircuit StreamProcessor where
delay b = Put b id
I reckon this solution to work for us as: we want someArrowCircuit >>> delay b
to be someArrowCircuit
delayed by one tick with b
coming before anything from it. It is easy to see we get what we want:
someArrowCircuit >>> delay b
= someArrowCircuit >>> Put b id
= Put b id . someArrowCircuit
= Put b (id . someArrowCircuit)
= Put b someArrowCircuit
Are there any laws for such a class? If I made no mistake writing delay
down, how does synchronous live alongside asynchronous?
The only law that I know of related to
ArrowCircuit
is actually for the similarArrowInit
class from Causal Commutative Arrows, which says thatdelay i *** delay j = delay (i,j)
. I'm pretty sure your version satisfies this (and it looks like a totally reasonable implementation), but it still feels a little strange considering thatStreamProcessor
isn't synchronous.Particularly, synchronous circuits follow a pattern of a single input producing a single output. For example, if you have a
Circuit a b
and provide it a value of typea
, then you will get one and only one outputb
. The "one-tick delay" thatdelay
introduces is thus a delay of one output by one step.But things are a little funky for asynchronous circuits. Let's consider an example:
Here,
multiplyOneThroughFive
produces 5 outputs for each input it receives. Now, consider the difference betweenmultiplyOneThroughFive >>> delay 100
anddelay 100 >>> multiplyOneThroughFive
:Inserting the
delay
at a different point in the circuit actually caused us to produce a different number of results. Indeed, it seems as if the circuit as a whole underwent a 5-tick delay instead of just a 1-tick delay. This would definitely be unexpected behavior in a synchronous environment!