jeremiep
Joined 14 years ago
Joined 14 years ago
Science is fantastic to dig into areas it can already see, and terrible at seeing new areas from the greater unknown.
The problem usually isn't react, but that most projects using it directly use it wrong.
This seems to be my biggest pet peeve with the JS ecosystem as a whole; people chase things that are easy to learn rather than simple to reason about once learned and the result is almost always a convoluted mess of a codebase, regardless of the project.
I used jQuery, then Underscore, and later lodash; now I'm using ClojureScript and wondering why I ever could enjoy any of those crippled FP alternatives in the past.
"Someone who doesn't know about X will create its own worse version" is true, but it also applies to you and everyone else :) We don't know what we don't know (thats meta-ignorance) and we tend to assume what we do know is the state of the art, it quite usually is very far from it!
Not everything on the web has to use the normal stacks. I used TypeScript in the past but didn't enjoy the complexity of most JS libs, even with typings. Clojure was a breath of fresh air, and still is.
Taxation is already high for the rich, but the top bracket is usually quite low. I don't think the solution is more taxes, but more tax brackets; they should scale to accommodate the extra rich.
I lived in poverty for years after dropping out of college, sometimes with roommates that were way beyond toxic, worked multiple jobs 70 hours a week just to pay rent and food and still ended up middle class with a job I love; I've been through hell to find heaven. What I learned on the way I now use every day, its made me a stronger and better person and I can now help others do the same.
If someone had given me what I have today, just for the sake of equality, I would not have learned responsibility, discipline, I would hardly have developed most of the skills I now have and probably would've lost all of it by now. I would basically still be an angsty teen in an adult's body, which is what kept me poor in the first place.
I forgot exactly where I got that number, but it's been a pretty good metric so far.
In a nutshell; the compiler is great a micro-optimizations and absolutely terrible at macro-optimizations. The former will get you a few percent of perf boosts while the later usually results in orders of magnitudes of performance gains.
Its near impossible to apply macro-optimizations at the end of a project without massive refactors.
Architecture, data structures, batching operations, memory locality, and a bunch of other metrics are all concepts the compiler can't really help you with whatsoever and they have a much larger impact on performance than the 10% the compiler is actually able to optimize.
The problem is that either programmers don't care, or they can't make the distinction between premature optimizations and architecture planning.
Have you read your own comment?
Where I have issues with that is it usually feels like an afterthought rather than a first-class feature.
If you instead model your application after an event log then your current state is just a fold over these events. You have the same list of actions you previously had to manage undo, but now its positioned at the very center of the architecture.
And then you start seeing emergent features of that design:
- you can time travel the UI's state, which is probably the best debugging tool you can have for UIs.
- you can replay the event log over fixed application code, which greatly decrease iteration times.
- it moves logic outside of the UI components, making the app easier to unit test.
- you're storing a sequence of intents rather than results; its effectively free to audit such a design, its convenient to report the last X actions performed with a bug report and more precise than asking the user what they were doing.
The list goes on and on. I believe most of the complexity in applications is generated from the fact there is very little synergy between the different features of these applications. If everything requires a different abstraction and set of indirections, then you're just accreting complexity over time.
If you have a design where features can emerge from it, its usually a safe-bet to say you're on to something :)
Thats how I've been building UIs for the last few years. `re-frame` in ClojureScript for one is a fantastic framework built on top of these concepts.
> I don't see the point of having a debate about the merits of functional programming vs. OOP in the middle of your post
I made that distinction to highlight the differences between retained-mode UIs versus immediate-mode UIs. There are more similarities to OOP vs FP than differences here.
Think of it this way; the application itself already contains all the state it needs. A retained-mode UI will deep copy that state into the UI components while an immediate-mode UI will make the UI state a function of the application state. There is huge value in doing that because it dramatically simplifies the UI stack, this translates into shorter iteration times, simpler reasoning and less bugs. Its no good thinking about higher level tooling if the fundamental API is overly complex.
> unless you build a system that's easy to understand on that end as well
We shouldn't care about whether something is easy or difficult; we're only going to learn it once after all. We should instead care about whether its simple or complex, because that is a direct function of our ability to reason about things once they are learned. Most things that are easy to learn generate more complexity than simplicity and I believe this is harmful in the long term.
> I don't think it's unreasonable to expect that the low-level rendering engine (ie. an implementation in UIKit) maintains some level of UI state.
You're right, its perfectly reasonable and precisely what React does under the hood. Where I disagree is that I don't want the cached UI state to leak into the components. Just like the current state of the DOM is irrelevant when rendering the virtual DOM. Its purely an optimization mechanism and one which should be allowed to evolve independently from the higher level components.
> Things generally aren't immutable
Then why are so many things moving towards immutability? Modern video game renderers push really hard to be stateless, to be completely decoupled from the game threads, to not leak cached state into entities and a bunch of other FP-inspired concepts.
The end result is incredibly more parallelism. The rendering pipelines are much simpler and performant. There are quite a few advantages to this approach. It also generates optimal memory layouts and accesses, these are simply emerging from such a design. Its much easier to have linear memory accesses if you constantly recreate the display lists in fresh memory. Updating a state tree is much more complex than creating a display list and it greatly cripples the potential optimizations.
> higher level libraries can easily remove the pain of UI state management
That is not the feeling I get when I look at the average React project; most components use getState/setState, need to touch the virtual DOM for almost every operation, are needlessly maintaining two views of the same state (app and UI) and whatnot. Or they go full Functional Components and the result is a huge stack of component layers from hell.
> It seems to me the appropriate abstraction is build the protocol around a UI state tree
I believe this is fundamentally wrong; the appropriate abstraction should be built around the application state. You can derive UI state from that, and doing so liberates you from having to maintain the UI state independently.