Yes, I noticed that similarity too a bit -- from the use of a "HyperScript"-like approach (although Mithril can be used with JSX too). https://mithril.js.org/hyperscript.html
Aberdeen is innovative in that it's great to be able to avoid the VDOM in a technical sense. For fun, years ago, I messed around with a different technical approach of just rendering HTML from code directly (similar to Aberdeen to that extent), but instead of an observables-ish idea, I used Mithril's approach of hooking into the event listening system to trigger redraws by wrapping event handlers. Of course, I quickly ran into the issue of losing state like cursor insertion points in editors when re-rendering (which is what motivates the vdom approach). I can wonder if maybe the HTML standard itself could be upgraded somehow to support that approach of replacement of HTML widgets but with retained widget state if an ID or such remains the same?
While I applaud Aberdeen as an experiment, with Aberdeen, it seems problematical developer ergonomics to have everything be a reactive pipeline of some form of observables or signals. It's not especially more terrible than many other systems (e.g. Angular in practice, encouraging the use of RxJS Observables as a new way of encouraging writing hard-to-understand code). As Aberdeen has the previously mentioned innovation of avoiding the vdom, overall it might be a win for people who like that style of coding.
But by comparison, I have found Mithril is so much easier to work with -- given the core idea of just assuming the UI is "dirty" after any listened-for event happens and re-rendering it.
It's true though that Angular has a "Zones" approach to redraw when state changes which hides a lot of complexity but has OK developer ergonomics similar to Mithril in that sense -- until you get mired in RxJS code encouraged by the platform (at least when I last used it seven or so years ago). And then Angular code get mired in various other Angular-isms which requires bloated code in practice with lots of files to do the simplest component. Yes, you can try to work around a proliferation of files, but that is not the Angular way, or at least was not several years ago. And Angular advocates would argue the standardization is a big win for larger projects with multiple developers. YMMV.
Also, dsego, thanks for submitting to HN in 2022 the essay I wrote on all that (which I just saw thinking prompted by seeing this article to check if it had already been submitted somehow): https://news.ycombinator.com/item?id=25194873
For others, this the essay which goes into more depth comparing developer ergonomics of React, Angular, and Mithril from my experience: "Why I prefer Mithril over Angular and React" https://github.com/pdfernhout/choose-mithril
For a simple Mithril example with no special Observable or signal magic, see, say: https://kevinfiol.com/blog/simple-state-management-in-mithri...
let count = 0;
const Counter = {
view: () =>
m('div',
m('h1', 'Counter'),
m('p', count),
m('button', { onclick: () => count += 1 }, '+'),
m('button', { onclick: () => count -= 1 }, '-')
)
};
m.mount(document.body, Counter);
The state is stored in just a regular JavaScript variable ("count" in this case), not a wrapped something or other special object (like Aberdeen or any other Observable or signal approach requires). This is made possible in Mithril in this case by the onclick method being transparently wrapped bu the HyperScript "m" function (or JSX equivalent) to call m.redraw() after the click event is handled. As with Mithril's HyperScript approach of leveraging JavaScript to generate HTML instead of inventing a weird non-standard templating system like JSX, Mithril's wrapping of event handlers leverages the ability to just simply define state in JavaScript closures or objects -- or whatever other more complex approach you want including even RxJS observables (as long as m.redraw() is called as needed if you make data changes outside an event handler). Lego Horie, Mithril's original inventor, just was brilliant with Mithril at creating great developer ergonomics. Here's what your Mithril examples looks like with Aberdeen:
let count = proxy(0);
function Counter() {
$('div', () => {
$('h1:Counter'),
$('p:'+count.value),
$('button:+', { click: () => count.value += 1 }),
$('button:-', { click: () => count.value -= 1 })
})
}
$(Counter);
// Or just Counter() would work in this case
The '.value' is needed because we can't proxy a number directly. This extra step goes a way if you have more data to store, like `person = {name: 'Peter', age: 42}`.Besides that, looks pretty similar yeah! Works rather differently though. :-)
Thanks for the example. I agree they look very similar, which is what dsego saw intuitively and commented on.
The difference in the two examples is essentially whether things need to be proxied or not.
If someone does not mind that proxying, then Aberdeen has an advantage over Mithril by avoiding the complexity of the vdom internally. So, presumably the rendering code is simpler, which may have some benefits in performance and also debuggability in some cases. You can get surprising vdom-code-related errors from Mithril sometimes for something misconfigured in a hyperscript "m" call. Also the vdom regeneration can sometimes have issues if you don't specify a "key" for each m() widget, needed sometimes so the vdom can be sure to replace DOM nodes efficiently and correctly.
If someone does mind the proxying requirement (like me), then Mithril has the advantage in that regard of seeming simpler to code for (without the data wrappers). Mithril applications also may be easier to debug in some other cases -- because you don't have an extra proxy levels as indirection in viewing your data model.
So, both approaches have their pros and cons depending on preferences and priorities.
> it seems problematical developer ergonomics to have everything be a reactive pipeline of some form of observables.
Why do you think that would hurt ergonomics? You can use whatever data structures you normally would (including typed class instances), you'll only need to proxy() them to be observable.
Thanks for the replies. Having to add "proxy" or similar everywhere is the ergonomics issue to me (others might disagree).