Functional Event Driven Programming
Asked Answered
A

1

6

I'm having trouble writing event driven GUI code in a functional style, using Clojure and Seesaw. Specifically, I can't figure out how to pass the state of the program around without using globals, or some other unpleasant hack. My current approach is something like this:

(defn event-handler [gui-state event]
   (update-gui! (get-new-state gui-state event)))

(defn update-gui! [gui-state]
   (remove-all-listeners (gui-state :button))
   (seesaw.core/listen (gui-state :button)
                       :action
                       (partial event-handler gui-state)))

It sets an event listener on the relevant component, with a partially applied function to advance the state and update the gui, including removing the old listener. Although this seems to be working, I don't really like it, partly because I can't pass the listener itself in the state (since it's not constructed until after I've already defined the state), so removing the old listener requires removing all listeners, which could cause problems as the program grows.

The closest solution I've found online is in this answer, but I don't know how to handle events as a stream like it shows. I'm sure there must be a better solution than my current approach, but I can't figure out what.

Can anyone show me how I can respond to user input events while still following a functional style?

Argonaut answered 14/11, 2014 at 1:12 Comment(0)
L
1

The Streams from the linked answer seem like an analog of core.async channels.

Instead of removing all listeners each event maybe pass in a channel that has event details put to it. The same channel should go to the button's logic handler where it will repeatedly be taken from.

Levasseur answered 25/7, 2015 at 2:50 Comment(1)
That seems to be working, and is pretty straightforward. Thanks.Argonaut

© 2022 - 2024 — McMap. All rights reserved.