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.
>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.
When you talk about an event emitter, what kind of code structures are you referring to? An EventEmitter a la Node.js is a pretty decent separation of IO and data, though not perfect: something external (perhaps an event loop) triggers subscribed code based on IO, but your code is given only data (payload) to process. Is your beef that 'someThing.onEvent(eventType, e => whatever)' is more 'OOP' than 'bindEvent(someThing.eventType, e => whatever)' is 'functional'? If so, how or why?
In general, it sounds like you might be referring to typical JS asynchronous IO (callbacks/promises/async-await and whatnot) when you talk about IO loops. But I can't for the life of me figure out what those things have to do with OOP vs functional programming, nor why using those structures requires you to store state on a "widget".
> 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?
I can say 'button.onClick(() => { button.clicked = true })' just as easily as I can chain every computation which would happen as a result of 'button.clicked' being true onto the event instead. The former is more OOP, colloquially, than the latter, but is definitely not inherent to UI programming: tons of popular patterns and frameworks exist whose primary goal is to remove the coupling between state and UI appearance.
> 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.
This has nothing to do with functional programming, or a functional style. Immutable data exists as a very common pattern in both colloquially-FP and colloquially-OOP languages, and so does mutable data.
At the point where you start talking about linked lists, I begin to think that you're misunderstanding or misusing some fundamental term we're discussing (Mutability? IO? Functional? Object? I'm not sure): a LL is a data structure (which is mutated by adding/removing elements, traditionally), and has absolutely nothing to do with programming style or paradigm.
> A UI widget cannot just be a function. A widget in-itself is data, the structure, the display, the color ... etc.
No, a data object can contain those things, but in higher-level UI programming, the data object is very very rarely the "widget" itself; it's just data that is supplied to the renderer (the API of which, in web UIs, is the DOM), and the renderer generates the UI based on that.
You can do that in a tightly coupled way and store all your state in the DOM, or you can use any one of many powerful systems that allow you to separate those and perform operations only on data, which can be applied to the external UI state outside of your code. React is a good example of this.
> HTML is basically pure data.
I think you're confused about the difference between HTML (text, can be generated and passed around by code) and the DOM (the API to what is being rendered by the browser). HTML is data, sure.
> You can still keep it functional if you have a function dynamically generate HTML based on a set of parameters (IO without a loop).
That's what event listeners and callbacks are. Whether or not you're dynamically generating HTML, or whether code is mutating it after it's loaded into the DOM, has nothing to do with functional-or-not. Code doesn't stop being 'functional' as soon as its goal is to alter the state of or handle input from some external thing, whether that's a DOM node or a traffic light.
I don't even know where you're getting all this from. I defined the "IO loop" the access of external state as both a read and a write. Once I defined it, it should be clear what I'm talking about. You should be aware of my definition through my explanation regardless of implementation. You go on to talk about asynchronous IO which is a whole different thing all together. I am not talking about concurrency. I am talking about IO from a very high level perspective.
Let's simplify it. Imagine an API that reads and writes strings from IO:
func read(source: str) -> str
func write(source: str, data: str) -> None
If you have both of these things in your program you have an IO loop. If you don't like the wording, give it another name. Either way, this "IO loop" is what I am addressing in the conversation. If I have a button widget that does IO, say... write data on click, then the most obvious way to create that widget is to unify the data describing that widget with a method that calls "write." When you unify data and methods you get an object. You understand now?>This has nothing to do with functional programming, or a functional style. Immutable data exists as a very common pattern in both colloquially-FP and colloquially-OOP languages, and so does mutable data.
This is completely wrong. Mutable data is not part of the functional programming paradigm. Here's the definition straight from wikipedia:
"In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."
>No, a data object can contain those things, but in higher-level UI programming, the data object is very very rarely the "widget" itself; it's just data that is supplied to the renderer (the API of which, in web UIs, is the DOM), and the renderer generates the UI based on that.
Obviously, we're not giving pixel level instructions (aka the actual widget) to the ui renderer. The renderer still needs to associate what's rendered with a function. A widget is not just a visual representation of something; it is an interactive visual representation. Data needs to be unified with methods to fully define a UI widget. Hence Object. As I will mention in other responses, this is the most obvious and common way of handling the UI problem. There are other methods that don't use OO. But a UI widget and what it does has a one to one match with the definition of an object in object oriented programming.
>I think you're confused about the difference between HTML (text, can be generated and passed around by code) and the DOM (the API to what is being rendered by the browser). HTML is data, sure.
I am perfectly clear about what I am talking about. There is absolutely no confusion. I am perfectly aware of what you just said. HTML is data, the DOM is an api that can manipulate said HTML.
>That's what event listeners and callbacks are. Whether or not you're dynamically generating HTML, or whether code is mutating it after it's loaded into the DOM, has nothing to do with functional-or-not. Code doesn't stop being 'functional' as soon as its goal is to alter the state of or handle input from some external thing, whether that's a DOM node or a traffic light.
When did I say the DOM was functional? I'm not talking about the DOM. I'm talking about UI development in general from a very generic perspective. I'm not looking at UI development soley from the perspective of javascript, HTML and the DOM. What I was talking about in the statement was this:
input_parameters = get_io_stuff()
renderUI(input_parameters)
Also this statement:>Code doesn't stop being 'functional' as soon as its goal is to alter the state
is completely and utterly wrong. Functional programming by its nature avoids all state changes. If it doesn't avoid state changes then it hides state changes through blackbox abstractions.
>I can say 'button.onClick(() => { button.clicked = true })' just as easily as I can chain every computation which would happen as a result of 'button.clicked' being true onto the event instead. The former is more OOP, colloquially, than the latter, but is definitely not inherent to UI programming: tons of popular patterns and frameworks exist whose primary goal is to remove the coupling between state and UI appearance.
This is exactly what I'm talking about. I never asked for the specific implementation in javascript, It's baffling why you bring it up. Here's what I'm saying: I'm saying the former OOP method is the most obvious method, the later method is using a global event emitter. The latter method is better because it makes the UI widget reusable across systems that utilize the same event emitter while the OOP method ties the handler and a reference to IO to the widget itself. Thats it. I think we can both agree that the majority of systems utilize the OOP method due to the prevalence of that style of programming.
>When you talk about an event emitter, what kind of code structures are you referring to? An EventEmitter a la Node.js is a pretty decent separation of IO and data, though not perfect: something external (perhaps an event loop) triggers subscribed code based on IO, but your code is given only data (payload) to process. Is your beef that 'someThing.onEvent(eventType, e => whatever)' is more 'OOP' than 'bindEvent(someThing.eventType, e => whatever)' is 'functional'? If so, how or why?
Yes. This is exactly what I'm talking about. I am also saying the latter method is better. It is better because it allows me to take that widget and reuse it in another project with a very different IO patterns. Imagine two implementations for button. We have code like this:
One implementation requires this: button.onClick(() => { button.clicked = true }) Another requires this: button.onClick(() => { button.color = "red" })
Try to think of onClick as just something that registers a handler to when the button is clicked. Ignore all the lower level details of what actually happens underneath and think of each as a method definition.
The code is not re-useable in general. Usually if I implemented the first requirement and hit the second requirement I have to write a lot of code to make an abstract version of button in which I can inherit two buttons with two different handlers.
Now look at this: button.onClick(() => { sys.triggerGlobalEventName("button.clicked") })
This makes the button reuse-able in any system that has sys.triggerGlobalEventName. The typical way to handle this issue is usually through inheritance and polymorphism. That style of abstraction leads to over complexity.