4 ms·
There is a core issue with UI development that makes things harder and less elegant than the backend. It's the io loop. Components must change state and react
by crimsonalucard 8y ago
There is a core issue with UI development that makes things harder and less elegant than the backend.
It's the io loop. Components must change state and react to state. This where the complexity arises. If you remove the loop by having components only react to state, you will find your programs to be simple, modular and more beautiful.
The feedback loop of IO destroys the functional and modular nature of UI. Functional programming simplifies programming through restriction, however ui development is inheritly an object oriented problem due to the IO loop, so the results of trying to make it functional will have limited benefites.
The way to make UI work is to find a way to have components react to state and change state in a simple way and without hard coding a refrence to external state in the component itself. Aka stateless.
Many people believe that passing a closure is the solution. The reality is passing a closure is awkward and a form of encapsulating state with methods that leads to the same issues of complexity that you get with oop. Keep data and functions seperate always, a closure is technically a functional concept, but it is also oop in disguise as it is instatiating state coupled with a functions just like an object in oop.
Global event emitters might be the best way. I'm not sure if redux does this. I'm not a UI guy so everything is just imho.
- coldtea 8y ago>however ui development is inheritly an object oriented problem due to the IO loop There's nothing OO about the IO loop. The main thing that calls for some OO in UIs is the widget hierarchy.
- crimsonalucard 8y agoIO loop necessitates OO unless you do some obscure tricks like passing closures and using a global event emitter. The most obvious way to implement UI is to do OOP. Widget hierarchy does not necessitate OO. Hierarchies can be built with plain data structures. OO comes into play when you place methods within the data structure. Methods coupled with data is essentially an object as described by OOP. Why would you need integrate a method inside data structures? Because IO.
- zbentley 8y agoThose claims are somewhat confusing. What is the difference, in your mind, between an IO loop and a "global event emitter"? That's all the IO loop is; an event emitter that fires events at subscribed code. Do you consider the "OOP"-ness to be evident in the fact that subscription functions (whether 'onclick' functions or 'fetch' callbacks) contain both code to react to events and an implicit binding to the IO loop? Or is your issue that some (not all!) DOM APIs happen to let you attach on-event functions to the same things you can read data out of? Basically, I don't see why UI programming is any more inherently OO or functional (assuming those are even opposites, which has historically been debated more or less to death) than any other domain. There are UI toolkits that subscribe to each and both of those paradigms, with varying amounts of rigor.
- crimsonalucard 8y agoWhen I talk about an IO loop, I mean external state. The program sends a message to external state, the external state sends a message back. That is a loop. If you get rid of one, you no longer have a loop. you just have IO. If you get rid of both you have just data and no IO. I am not talking about abstractions on top of this loop. A global event emitter is such an abstraction that one can use to hide and block off the impurity of IO from the rest of your code. The most primitive form of IO is the socket. >Do you consider the "OOP"-ness to be evident in the fact that subscription functions (whether 'onclick' functions or 'fetch' callbacks) contain both code to react to events and an implicit binding to the IO loop? Or is your issue that some (not all!) DOM APIs happen to let you attach on-event functions to the same things you can read data out of? Look at where events come from and where they are sent. How do you associate the action of button A with the data representing button A? The most obvious way is to merge the action and the data into a single primitive. An object as described by OOP. You will have to do this regardless of what an API such as the DOM offers as primitives. >(assuming those are even opposites, which has historically been debated more or less to death) They are opposites. This is only up for debate for people who don't truly understand the differences. In OOP as described by JAVA or C++ you have a graph of mute-able objects instantiating other mutable objects and modifying each other. In functional you have a linked list of composed immutable functions with an input on the head and an output on the tail. Nothing is mutated. To get functional working with UI you need to employ obscure tricks as a UI widget is more of an object than it is a function. They are opposites according to the most common definition of OOP. They are only orthogonal if you get rid of mutation which is NOT the way most OOP is used nowadays. >Basically, I don't see why UI programming is any more inherently OO or functional A UI widget cannot just be a function. A widget in-itself is data, the structure, the display, the color ... etc. And It must react to state and IO like a function. It is both data and methods and thus OO. Without IO the the widget can just be a plain data structure. HTML is basically pure data. You can still keep it functional if you have a function dynamically generate HTML based on a set of parameters (IO without a loop). However when you need to introduce the "loop" and have UI both send and receive data that's when methods need to be attached to the UI widgets.
- arvinsim 8y ago> Global event emitters might be the best way. I'm not sure if redux does this. I think Redux actions are essentially emitted global events.
- acemarke 8y agoThat's one way to look at them, yes. Redux's `store.subscribe()` function is obviously a general event emitter, where the only event is "an action was dispatched", so it doesn't even need a name. On the actions side, Redux really only has a single "root reducer" function. Since having a gigantic monolithic function for all state updates would be unmaintainable, we split that function up into smaller functions, like any other piece of code. Since Redux was based on the existing "Flux" concepts, the idea of having multiple "stores" for each different type of data was translated into having multiple "slice reducers", each one responsible for independently updating the data at a given key in your state object. Redux provides a `combineReducers` utility for this use case, which iterates through all the keys in the state object and calls the slice reducer at each key. If you squint at this reducer setup the right way, you can see it as being a limited pubsub event system as well. So, it's reasonable to view a Redux store as being a 2-way pubsub system. Different parts of the app dispatch an action to "publish" an event to the mostly-independent slice reducer functions, and the store publishes a single event to let any part of the UI know that the state _may_ have been updated.
- np_tedious 8y agoI don't think many people actively working on front end will agree with passing closures as the best way. "callback hell" is a very common complaint. What you've discussed above seems to overlap pretty well with both redux and monadic state management like Elm.
- np_tedious 8y agoHmm maybe elm isn't really monads. Tried to bullshit there, my bad. https://redux.js.org/introduction/prior-art#elm https://redux.js.org/introduction/prior-art#elm