Closed RiugaBachi closed 5 years ago
Good find! I'll have to investigate this. In the meanwhile I would also accept a PR :)
After thinking about it for a while, I believe it isn't possible to write ArrowApply
for monadic automatons such as VarT
.
First of all, any Arrow
that implements ArrowApply
can technically be re-written as a Monad
. Now, I have yet to see a pure monadic FRP library. All monadic FRP thus far relies on IO/ST; prominent names would obviously include reflex
and frpnow
. I'd like to think of 'monadic FRP' in this case as more of a reactive programming framework than "FRP", but I digress.
It would be highly problematic if monadic automatons such as VarT can be expressed as a Monad
, which would render all of the research & development on convoluted arrowized FRP design everyone's been working on for the past decade and a half, nil. I don't think we would find ourselves using Arrows these days if pure FRP could be succinctly expressed via Monad
s.
Secondly, the expanded type of app
for a monadic automaton A
is roughly:
((b -> m (c, A m b c)), b) -> m (c, (b -> m (c, A m b c), b) -> m (c, ...))
The area of concern is the second tuple element of the output parameter. In particular:
A $ \(innerAutomaton, input) -> runAutomaton innerAutomaton input >>= pure . (, ???) . fst
What causes most instances to break is trying to find an arrow that can meet the type of ???
; often times people will give up and 'go with the obvious' which would be to pass in app
, but then we aren't using the second tuple element for what it is intended for: persisting state. Passing in app
as is - which is how VarT
does it - does not define an arrow returning the next step result as it should, even though it typechecks; and if we shouldn't be using app
here, how else would we define an arrow that will both typecheck and persist state?
I suppose in summary, we could look at it this way: can we define a Monad equivalent for VarT
? If yes, then ArrowApply
can be lawfully defined. If not, then it is impossible to define a lawful ArrowApply
instance. Coming up with a monadic equivalent is probably easier to reason about than trying to figure out a state-persisting alternative for app
.
Yes, there is no way to define Monad
for VarT
as it's not sequenceable. Hence the entire Spline
module. I'll remove the ArrowApply
instance - I don't remember writing it and it's possible I cargo culted it from another FRP lib. Thank you so much for figuring this all out!
Uploaded to hackage.
:bowing_man:
Using
app
notation in aproc
block, that is to say,Where
someOtherThing :: a -> VarT m () b
, makesout2
's state reset upon the next step of the automaton. Meaning,out2
could be updated to contain a single element[ () ]
, and the trace would show that, but on the nextrunVarT
, it will reset to[]
. Rewriting this to avoidapp
notation by passing in a tuple((), out1)
as input fixes this state persistence issue.I believe the concensus is that it is impossible to implement ArrowApply for an automaton-esque structure like VarT. I've seen some solutions that rely on IO/STRefs out in the wild, but this is against the purity of
varying
. I'm not sure if something hacky with an internalState
monad could do the equivalent trick, but otherwise, it may be best to remove this instance to prevent confusion.