Here is the problem: a GUI button has a callback that toggles its state from checked to unchecked.
In imperative programming languages, it is very easy to implement: just modify the Button's state. For example:
void callback(Button btn) {
btn.setChecked(!btn.getChecked());
}
But in pure functional programming, a callback cannot change the state of an object. The only thing the callback can do is create a new Button object with the new state. For example:
Button callback(Button btn) {
return new Button(!btn.checked);
}
The button created by the above callback will not be part of the program's GUI, because an external function would have to get the result of the callback and reintegrate the new button value to the GUI.
Furthermore, a button shall not have callbacks with the above type signature, because button callbacks should be generic. I.e. the type signature of a callback would be:
Object callback(Object object);
The only solution I can think of in purely functional code is for callbacks to accept and return the global GUI, e.g.:
GUI callback(GUI gui, Button btn) {
...bla bla bla recreate the gui tre ...
}
So how do I do this in purely functional code? how can my purely functional callback change my button's state?
Probably the most elegant way to design GUIs in a pure functional way is Functional Reactive Programming - see also this SO question and the HaskellWiki page.
If you do things that way then you wouldn't be using callbacks per se, but defining functions that explain how "behaviours" should evolve with time and user input. A button click would be an "event" which would influence the relevant behaviours.
If you want to stick with a more traditional callback model, then I think some degree of imperative behaviour is necessary.
However you can keep the amount of mutability to a minimum by for example having just a single top-level mutable value that represents the entire current state of the program, perhaps somewhat similar to the GUI
type you suggested above. The value would be of some complex algebraic datatype that holds everything that can change.
You can also use abstractions that constrain access to that top-level state, e.g. by limiting access to only particular parts of it and only giving access to those abstractions to particular callbacks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With