zrho / purescript-optic-ui

PureScript UI framework based on lenses.
122 stars 10 forks source link

Composing calls to runhandler #10

Open FrigoEU opened 8 years ago

FrigoEU commented 8 years ago

Hey,

I've been using optic-ui for a small project and so far really liking it. I have a lot to learn in terms of how to build reusable components with all the facilities that both purescript and optic-ui offer, but one thing I keep running into is the fact that I can't do two runhandler invocations one after another.

I'm taking some cues from Elm by making a "handle" function that I pass down in the hierarchy and feed "commands" that then changes the state of a higher lying component. However I often want to do two things in response to an event: update the state of the current component AND call the handle function that got passed down. When I want to do it with simple do syntax like this:

do 
  runHandler h $ set _foo "bar"
  handle MyCommand

This doesn't work because of the gen counter in OpticUI.Run. I would make a PR if I had an idea how to fix this, but I don't... Any help much appreciated.

zrho commented 8 years ago

Calling multiple handlers for the same state is problematic: Since there isn't really a viable way to have non-labelled stable paths into the state, a handler ultimately always carries implicitly a recipe how to reconstruct the entire application state when the state of a small component has changed. Basically a template where only the focused state is missing. If one were to call two handlers on the same state, even if the focused substates are morally independent, the second handler will revert the changes of the first.

This is unfortunate, of course, since - as you say - there are legitimate use cases for this: imagine a button component that takes an action to execute on click, but that also disables itself on click. If the action that has been passed to the button contains an invocation to a handler, the change of the state of the button will either revert the action of that handler (no gen counter) or will not occur at all (gen counter).

I currently don't have a clear idea how to solve this, but this and related issues are a pretty high priority to solve. Feel free to let me know of any ideas you might have, so we can work on a solution together.

zrho commented 8 years ago

Have a look at d5e0cec9c9adce51d65e3c24442ff9e0a6f58568, especially the withEffects function and the effects example. This does not directly allow to compose handlers like in your example, but may allow to model your use-cases in a different manner. What do you think?

FrigoEU commented 8 years ago

I'll need to think about how my use case could be solved by this, but I definitely think it's a nice addition. I'm a little bit concerned with the ability to have hidden state like you showed in the effects example, but I guess it's up to the user to use this responsibly... Definitely think it's a nice addition, just need to figure out exactly what I can do with it :).