Earlier quoted context omitted.
When 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…
That's a baffling definition of IO and IO loops. It sounds like you think bidirectional IO is an IO loop, as opposed to unidirectional IO (which . . . does that even exist outside of "write bytes here" shared memory constructs? Even UDP write operations get data back from the kernel), but you also classify a socket, which is typically bidirectional, as just "IO", so I'm not quite sure what to make of that. It sort of…
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.