It is the same Xiaomi which announced 300 000 SU7 vehicles sold in a day and 6 months later reported they are struggling to hit their sales targets? Just don’t believe anything a Chinese company say, divide it by at least 2. I have nothing against China, this is a cultural thing which you should be aware reading anything coming from the country.
Xiaomi EVs sales growing ~15% YoY, they just likely to miss their wildly ambitious ~33% YoY target. Or they might get close, they're 80% of target for H1, but PRC sells more cars in H2 (no Q1 new years, Q3+Q4 big sales).
But it's also the same xiaomi and sends gold samples and hacks gaming benchmarks, but IRL an off the shelf model is merely 80% better instead of 100%. It's no different than Samsung, Apple, Nvidia curious powerpoint axis. Whatever advertised, reality is slightly worse, but rarely substantially.
There was research showing how AI performance degrades as you go from green field into support. My guess ERP in question will break after some time as agent will add more bugs than new features. Plus recovering data will be extremely difficult.
There's got to be a better way of saying this. "A policy is good if it keeps being good after the first time you use it" is the gist that I'm getting, but that's way too simplistic to actually tell you any of the value of dynamic programming.
You can say it this way. Other way to put is optimal policy makes optimal decisions on every step as if it knows the future. If you looking for shortest distance in a graph then at every vertex it picks the right step, even if the edge is longer than other available in the vertex, as if it knows the future. This is why you cannot just build an algo implementing the policy, you have to find it with dynamic programming.
I like drag&drop way more than .pkg. just easier to deal with. If you do a CLI tool then maybe `brew install CLI` is the way to go instead of a pkg file?
MVC is not bad, but it is not a silver bullet. Calling MVC an ultimate solution to UI is oversimplification. Just looking at the steps you listed I can ask:
How do you collect all notifications on step 2 to fire them on step 3 such that UI does not re-render itself too much? E.g. updating a title of each item in a list of 100 items should not trigger 100 renders. Or 100 layout calculations (which I think is harder to avoid).
How do you deal with situations where on step 4 UI triggers an event that your model happens to listen and the cycle repeats while killing performance?
Because you rely on events how do you avoid “event hell”? That is, a situation when an event handler triggers a change that triggers another event handler that triggers a change and so on. Sometimes it is scrolling or typing, sometimes it is parts of the model subscribed to each other bubbling events to UI.
I never claimed MVC is a silver bullet. Just that it solves "... incredibly easy to forget an edge case in your update logic."
> UI does not re-render itself too much?
Glad you asked! In my Blackbird reference architecture (which is an instance of MVC), I use a coalescing queue to capture the updates. The coalescing is two-level: first, simple duplicates are weeded out. Second, if the update queue gets very full, it becomes coarser-grained, and weeds out duplicates based on that coarser grain. This has multiple steps of grain up to "just re-render the whole UI". Worked like magic in Wunderlist. Except it wasn't magic at all and very simple, inspectable and tractable.
> step 4 UI triggers an event that your model happens to listen
That's not allowed in MVC.
> Because you rely on events how do you avoid “event hell”?
I don't "rely" on events and there is no "event hell". Events are only used in the M→V communication part and there are no subsequent triggers, because the only event is "the model has changed", with an optional payload specifying which part of the model. Important: it must not contain the data that changed, this the view has to fetch from the model once it processes the update event.
Since the only event used is "the model changed", the view cannot ever be a source of those events, so no "event hell".
How do you handle UI state vs. underlying data (model) state, and dependencies between them? By UI state, I mean things like scrollbar position and selection state. When displaying a scrollable and selectable list of items, then for example when the number of items changes, the selection may need to adjust, and the scroll position may need to adjust. Depending on which items are added or removed (or reordered), the selection and scroll position may need to change differently for the apparent UI state to look stable for the user. If only the model is changed, a previous UI state like selection or scroll position may become invalid in relation to the new model state. Who updates the UI state accordingly to make it valid again? In the general case, application code needs to be involved in choosing the desired valid UI state when the underlying model state changes. How is the corresponding application code prevented from triggering further events?
When you have stateful view objects, these stateful view objects maintain the view state. When updating themselves with new data due to a ModelDidChange notification, they take care of reconciling their current display state with the underlying model state.
> When displaying a scrollable and selectable list of items
So for example an NSTableView or NSCollectionView. I personally use a subclass that interacts directly with a table representation, meaning a lot of the glue code that Cocoa(Touch) requires disappears.
> Who updates the UI state accordingly to make it valid again?
Always the view. Who else?
> In the general case, application code needs to be involved in choosing the desired valid UI state when the underlying model state changes.
How so? The view is always a reflection of the model data. Whether that is a "change" is actually mostly irrelevant, even though the notification is called ModelDidChange in my case. In Smalltalk MVC it is the #changed message. It means "you are out of date, please make yourself reflect the model".
This same mechanism also handles the model being changed by some other party without any further code. "The model has changed, please update yourself to reflect the current state of the model". That's it, modulo optimizations.
> How is the corresponding application code prevented from triggering further events?
Model code isn't involved. A ModelDidChange event is only triggered when...er...the model changes.
That said nothing prevents you from manually invoking the ModelDidChange notification, just like nothing prevents you from calling abort(), running an infinite loop, creating an unbounded recursion or reading from /dev/random until it is exhausted ...
Doing it by accident, though, is very hard, because it just isn't part of the programming model.
>The coalescing is two-level: first, simple duplicates are weeded out. Second, if the update queue gets very full, it becomes coarser-grained, and weeds out duplicates based on that coarser grain
This is not about duplicates. For example, sync updates 100 items in a list changing their titles. Items are bound to a list in the UI. Thus, 100 unique title update events triggered.
>Events are only used in the M→V communication
I don’t understand. Button clicked -> model change -> view update -> new event triggered -> model or view updated again … This is not something one would code on purpose, but often an attempt to create relationships between view. Like a custom layout code. Might not include model at all, just views being updated in an event handler trigger more events and more updates to views.
> For example, sync updates 100 items in a list changing their titles. Items are bound to a list in the UI. Thus, 100 unique title update events triggered.
Those "updates" go in the queue. When the UI gets around to updating itself, it looks at the queue and invalidates all the UI elements that refer to the model items in the queue.
It then updates those elements, using the coarsening to update larger elements in bulk if that becomes better.
> Button clicked -> model change -> view update -> new event triggered -> model or view updated again
Once again, that is not allowed. View updates are not allowed to trigger any events in MVC. A model → view update updates the view. That's it.
The only event is "model changed", so it also doesn't make sense for the view to generate those events.
I can only say how I did this in the Azul GUI framework[1] (note: not production ready yet), which may be close to what you're describing. So in Azul, you do this:
So, there's no "automatic" re-render, a callback has to return "Update.RefreshDom" or "Update.DoNothing" (default).
Now to your questions:
> How do you collect all notifications on step 2 to fire them on step 3 such that UI does not re-render itself too much?
Diffing, and then caching very aggressively. The click causes the model to re-call the layout() fn to return the entire DOM, however, there are ways to make this step very fast (arena allocation / no allocation). Then this gets diffed with the previous DOM state and the framework internally reuses everything it can (with user providing keys for list items, like React does).
> How do you deal with situations where on step 4 UI triggers an event that your model happens to listen and the cycle repeats while killing performance?
Azul has a "max recursion depth" of 5 and then just throws an error (infinite cycle). So, it will invoke all the relevant callbacks for a frame, then "sum up" all of the Update enums (i.e. one callback returned RefreshDom -> now we need to repaint).
> Sometimes it is scrolling or typing, sometimes it is parts of the model subscribed to each other bubbling events to UI.
Scrolling, selection, typing, etc. are handled by the framework. To make something editable, you need to set "contenteditable=true" on the Dom node (like on the web). Then, on text editing (which can also come from IME, a11y input, copy-paste), you get a "text changeset". The callback can then "reject" the changeset or allow it (default, since you already set contenteditable before).
Azul has a "dual update pattern" for performance here, i.e. the DOM itself is immutable until the next layout() call, however for "quick edits" like dragging a node you obviously don't want to call layout() again and construct an entire new DOM tree. So there, you just (conceptually, don't know the current API for this):
def on_div_dragged(data, info):
mouse = info.get_window_state().mouse_state
info.set_css_property(info.get_hit_node(), "transform: translate(%s, %s)", mouse_state.x, mouse_state.y)
# store in data model or node if necessary
data.user_mouse_pos = mouse_state
return Update.DoNothing # no re-render here
So, if another callback fires in between, the data model is still properly up to date. Azul also aggressively reconciles focus, scroll position, selection, text cursor position, etc. But Azul does not allow "one event auto-triggers another" like SolidJS does, it looks nice on a slide deck and then is a pain to debug Rube-Goldberg state machines.
This also works for text input or updating images (i.e. you don't need to call layout again on text input). Update.RefreshDom is for "larger / structural" changes, i.e. something like a route switch in a SPA-style app. Azul tracks the text cursor position by diffing the actual text, so the user code doesn't have to track the text cursor and state is preserved during a diff (it can also retain heavy elements).
For large lists, there is a native "virtualized view" DOM node with a callback that is being called "during" layout (after the size of the container has been determined, then the framework asks you to render your DOM, given the scroll position). So, that can be diffed, too. You never render in the DOM more than ends up on screen, so the perf is manageable.
Scrolling and retaining scroll positions inside a virtualized view is still an ongoing topic (not impossible, you just have to have functions to measure the DOM items before you return them, to estimate how much you need to render, and then do the math for "where are we right now, where is the scrollbar, how big is the virtualized view in relation to what we're rendering" - so the framework can set the right scrollbar size and position).
Again: please don't use or post Azul here on HN yet, docs are still slop and undergoing review, API is unstable until I have some apps going, but I just wanted to answer these questions.
Famous UI = f(Model) is oversimplification that was sold in slides. Real “functional UI” frameworks implement UI = f(Model, UIState) where UIState is scroll and cursor positions, view pool for virtualization, rendering caches, etc. USState is mutable and managed by the framework and the rendering engine (e.g. React + DOM, SwiftUI +
UIKit + CoreAnimation). I don’t see a problem with functional approach as in React. I do see a problem with understanding of how UIState being managed between framework, ui library and rendering engine.
reply