Closed anttih closed 6 years ago
Well the reason why I didn't spend much time on writing a MonadRec instance is that I'm not sure it is needed. Since this implementation is based on React, and uses an effectful setState to traverse the binds, it basically implements a trampoline for recursive computations. I am about 95% sure that this means that rendering a widget will never have problems.
Please let me know if you think that I'm not thinking this through properly, and have missed some case.
But even the basic counter example (pun not intended):
counter count = do
newCount <- button $> count + 1
counter newCount
will blow the stack, right? You'd want to call tailRecM
there to do the trampolining. It would also be helpful to have forever
available to loop at the top level.
Ps. I actually have POC implementation of halogen-vdom so it would be nice to not rely on react's implementation details.
Actually no. That example should not blow the stack for the reasons I mentioned. Do notation basically desugars to binds, and the way bind for widgets is currently implemented, recursive calls do not increase the stack.
Also, while this implementation is react specific, I imagine it's possible to abstract it in a way to make this safe in other backends as well. I'll start thinking about how to do that.
The halogen-vdom implementation sounds cool! Do you have a repo somewhere?
Interesting, indeed that does seem to be stack-safe.
I see your point about the react implementation being stack-safe. But how would you run a StateT
widget forever? StateT
itself is MonadRec
but it relies on the underlying m
to be one as well. I think you could do it but you would need to unwrap and loop the StateT
in "userland" code. Basically if we had the MonadRec
instance a lot of the monad transformers usage would be much easier. You could just stick the forever
call at the top.
The vdom version is not anywhere yet.
I see your point. As a short term solution, I will add a trivial MonadRec instance. How does this look? -
instance renderMonadRec :: MonadRec (Widget v eff) where
tailRecM f a = go =<< f a
where
go (Loop a') = tailRecM f a'
go (Done b) = pure b
Nice! It was that simple after all. So this should be stack-safe as long as the backend is stack-safe?
Yup! It's a trivial instance that offloads all the work to the backend.
Ship it!
Do you have ideas on how to implement this? It seems to me that you cannot do this with the current formulation of the
Widget
datatype. I also tried usingFree
but wasn't able to yet write theAlt
instance for that.Free
uses a sequential datastructure for the binds, maybe this approach could work here as well. I'm not sure.