sharkdp / purescript-flare

A special-purpose UI library for Purescript
287 stars 17 forks source link

how to sequence effects with flare UI? #14

Closed joelgrus closed 8 years ago

joelgrus commented 8 years ago

Say I would like a slider to choose a number n and then plot n random points.

I can do this easily if the points are deterministic, but once they're random I need to generate them in an Eff (random :: RANDOM | e) context, and it's not obvious to me how to sequence that with the UI applicative. (I tried a number of things, but none of them worked.)

See example (and non-example): https://gist.github.com/joelgrus/b27c12fc159367ea2440

I hope this is the right venue to ask this kind of question. Thanks!

sharkdp commented 8 years ago

I hope this is the right venue to ask this kind of question.

Sure!

As you pointed out, the issue is that runFlareDrawing expects a UI Drawing, i.e. some interface that produces a pure value (a Drawing). As you have to run an effectful computation to generate that Drawing, you cannot use runFlareDrawing.

You can solve this by using runFlareWith instead (in your case, a = Int):

runFlareWith :: forall e. ElementId
             -> (a -> Eff (dom :: DOM, channel :: CHANNEL | e) Unit)
             -> UI e a
             -> Eff (dom :: DOM, channel :: CHANNEL | e) Unit

As the second argument, this expects a custom function that can run any effectful computation (for you, eff includes RANDOM and Canvas). You can provide your own rendering function, something like:

drawRandomPoints :: forall eff. Context2D
                 -> Int
                 -> Eff (random :: RANDOM, canvas :: Canvas | eff) Unit
drawRandomPoints ctx n = do
  clearRect ctx { x: 0.0, y: 0.0, w: width, h: height }

  drawing <- randomPoints n
  render ctx drawing

Finally, in your main function, you would call runFlareWith instead:

main = do
  Just canvas <- getCanvasElementById "canvas"
  ctx <- getContext2D canvas

  let ui = intRange "numPoints" 1 100 10

  runFlareWith "controls" (drawRandomPoints ctx) ui

You can try it out here: http://sharkdp.github.io/try-flare/?gist=bdb4548abb6103be3e2a

sharkdp commented 8 years ago

@joelgrus Can this be closed?

oblitum commented 8 years ago

Question and answer were very helpful.

oblitum commented 8 years ago

An updated sample for this using latest purescript would be nice to have.

oblitum commented 8 years ago

I've added it as example 17 in this gist: https://gist.github.com/oblitum/90cbf5338f55321a9d975f0588a711cd

oblitum commented 8 years ago

I've finished a more complete sample which I incorporated in a blog post, it's still small:

You may feel free to incorporate it to the repository. Consider it public domain.

Notice that for now this one has moved the random computation outside of the controller, example 17 above is more correct in this aspect. Now... it seems I got back to this issue. How can I get the reset button to always reset the random points to a new initial random state instead of a static one... (since I'm managing model's state through usage of foldp, which is UI code, not controller)?

oblitum commented 8 years ago

I've noticed also that that technique of using foldp to start/reset the animation while having an internal animationFrame seems to turn framerate quiter slower (running just the view instead of the resetView is faster). Sadly I dunno how to do the same (having start and reset state) in a different way (not running animationFrame internally to a foldp).

@sharkdp I'd like to know your judgement regarding this. I'm feeling I'm already hitting the limitations with Flare here (I'm not sure)? I'm just starting in FP again because of PureScript but the way I've done state management of buttons seems a bit contrived, restrictive and resource hungry (foldp with animationFrame). And I really wished to get back to the state of example 17, with a effectful computation (random) as the controller, while also, having the buttons and animation working like they are currently.

sharkdp commented 8 years ago

Sorry for the late answer.

I've finished a more complete sample which I incorporated in a blog post

Very nice!

You may feel free to incorporate it to the repository. Consider it public domain.

Thanks, I've added a link in the README.

How can I get the reset button to always reset the random points to a new initial random state instead of a static one...

has this been resolved in the mean time?

oblitum commented 8 years ago

has this been resolved in the mean time?

No, I haven't touched the code since then, but I'd be interested in a flare sample of that, I'm still at https://github.com/sharkdp/purescript-flare/issues/14#issuecomment-228087969 state.

Right now I'm trying to finish "PureScript by Example", so in those samples I touched subjects which I was not familiar with. After the book I'd go about studing other libs and flare to check whether I've hit the limit with flare and it would be more natural using pux, or other lib.

oblitum commented 8 years ago

Thanks, I've added a link in the README.

Thanks!

sharkdp commented 8 years ago

Sadly I dunno how to do the same (having start and reset state) in a different way (not running animationFrame internally to a foldp).

Currently, I cannot think of any other way, either. I was thinking about saving a "reset time" every time you press "reset", but that would also require you to pass the animationFrame signal through foldp.

You could probably nest two Flare UIs. The outer one would completely re-start the inner Flare, whenever "reset" is clicked. But ...

I'm feeling I'm already hitting the limitations with Flare here (I'm not sure)?

Quite possible. I encourage you to try out different PureScript UI frameworks. Flare is really only suited for "spreadsheet-like" UIs and the only way to incorporate "mutable state" is by using foldp.

I'm closing this, as the initial issue is resolved, I think. But let me know if you find out something else.