schell / varying

Continuously varying values, made easy :)
MIT License
40 stars 5 forks source link

Broken ArrowApply Instance #37

Closed RiugaBachi closed 5 years ago

RiugaBachi commented 5 years ago

Using app notation in a proc block, that is to say,

out1 <- foldStream (flip (:)) [] . something -< ()
out2 <- foldStream (flip (:)) [] . someOtherThing out1 -<< ()
varM trace -< show out2

Where someOtherThing :: a -> VarT m () b, makes out2'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 next runVarT, it will reset to []. Rewriting this to avoid app 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 internal State monad could do the equivalent trick, but otherwise, it may be best to remove this instance to prevent confusion.

schell commented 5 years ago

Good find! I'll have to investigate this. In the meanwhile I would also accept a PR :)

schell commented 5 years ago

(for me) https://stackoverflow.com/questions/27603108/what-is-wrong-with-this-instance-arrowapply-automaton

RiugaBachi commented 5 years ago

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 Monads.

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.

schell commented 5 years ago

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!

schell commented 5 years ago

Uploaded to hackage.

:bowing_man: