stage0 icon indicating copy to clipboard operation
stage0 copied to clipboard

Comparison to Solid, Sinous, Svelte, etc? And using stage0 with ReScript/ReasonML

Open d4h0 opened this issue 3 years ago • 4 comments

stage0 looks amazing!

I'm wondering what the advantages and disadvantages are to similar libraries/frameworks.

Would it be possible to create a comparison?

I'm interested in using a library that doesn't use a vDOM with ReScript/ReasonML. The problem is, that Svelte, Solid and most other libraries use a compiler, that makes it difficult to use in that context.

It seems, stage0 should be relatively easy to use with ReScript, but I'm wondering what disadvantages (and advantages) there are, compared to a compiler-based library.

What I know so far:

  • It seems stage0 is more verbose to write than Svelte and Solid (but I don't really see a problem here, personally)
  • It seems, stage0 doesn't support sCU optimizations. However, I don't really know what this is*, and what the actual disadvantages are. It seems this limitation doesn't hurt stage0's ranking in the benchmarks. (* It seems sCU stands for "shouldComponentUpdate", which gives me some idea, but I'm not sure about the significance)

I'm also wondering what the development status of stage0 is (it seems there wasn't an update to the code since two years). Is stage0 "complete", or would it be a bad idea to use it for new projects?

d4h0 avatar Nov 10 '20 15:11 d4h0

Personally I look at this as matter of how close to the metal you want to get to. I'm a big fan of stage0 in its minimalist approach. It gives you the tools you need to build interactive UIs without pushing some opinionated structure on you. You could implement a shouldComponentUpdate like mechanism if you wanted to. You write the update function, so you write your own update guards.

Most benchmarks have 1 component in them.. or at most 2 layers.. sCU is important once you are building larger applications where there is nesting. You already start to see that effect with one layer in the JS Framework Benchmark when you look at selection test (the only test doing a delegated state update) and add a few more layers and this can become more prevalent depending on the application's data architecture.

Reactive library like Svelte and Solid go a step further in that their reactive system is a built-in form of memoization so no one manually writes shouldComponentUpdate functions but rather the library takes care of all of that for you unlike VDOM libraries like React.

But as I said you can implement that yourself with stage0. Stage0 is a library not really trying to be a framework like Solid or Svelte. Think of it more like comparing jQuery to React. You wire everything up and it gives you all the tools you need to be efficient. You can completely develop rich applications and component libraries this way. The abstraction is just minimal you are fetching DOM elements running helpers and writing imperative DOM update code yourself.

Honestly, I recommend just trying it out. Look at the TodoMVC example(https://github.com/Freak613/stage0/blob/master/examples/todomvc/app.js) and picture yourself writing those update() functions. If that is inline with your thinking, writing your updates and writing your own guards (using the closure and storing the previous values) then this could be the thing for you.

It doesn't get more minimalist and in an experienced programmers hands it's very difficult to beat the performance of hand written optimization like this. It's also much easier to use stage0 than to write all the code yourself. Stage0's helpers are first rate proven approaches used by the most performant libraries so you can't go wrong if you know what you are doing.

ryansolid avatar Nov 10 '20 18:11 ryansolid

@ryansolid, thank you so much for your detailed and insightful response!

I followed your recommendation and read the TodoMVC example. To be honest, I like the approach of stage0 even more now (however that might change if I start to actually use it – who knows).

I like that everything is more explicit and direct, and that there is not as much magic as with React and Svelte (the frameworks that I have used most).

Ergonomics seem not as bad as I thought. I compared stage0's TodoMVC implementation to Solid's, and ergonomics seem similar. stage0 has fewer lines of code/characters, but Solid has slightly more features (mostly, persistent storage and editing of todo items, which would be easy to implement).

A big chunk of Solid's code is simple (but verbose) JSX, so someone could argue Solid is less complex (because it basically can do what stage0 does in 1/2 or 2/3 of actually "complex" code). However, it was significant easier for me to follow stage0's implementation. I assume, because the code is more direct and vanilla JS (this advantage could disappear if I'd use Solid more, I guess).

I like how stage0 implements templates/views. This reminds me a bit of the simplicity of Mustache templates (it's actually even simpler).

However, what I like most about stage0 is that it seems to be implemented in only 800 lines of code, without any dependencies (if I'm not missing anything). That means, I probably could relatively easily rewrite stage0 in ReasonML or OCaml.

And – to be completely honest – a reactive UI framework/library without vDOM implemented purely in OCaml or a derivate would make me ecstatic... 😁

It should be possible to improve ergonomics even more (I already have a few ideas), and there is a chance that this port would be faster than stage0 itself (the OCaml-to-JavaScript compiler generates optimized code).

Unfortunate, I have other things to do and can't jump on this for at least a few weeks or months... 😒

Most benchmarks have 1 component in them.. or at most 2 layers.. sCU is important once you are building larger applications where there is nesting. You already start to see that effect with one layer in the JS Framework Benchmark when you look at selection test (the only test doing a delegated state update) and add a few more layers and this can become more prevalent depending on the application's data architecture.

During my research I found several interesting articles written by you. Did you write anything about the optimizations that Solid does in regard to efficient layered component re-rendering? That probably would be interesting, if I decide to port stage0.

Thanks again, for answering my question!

d4h0 avatar Nov 12 '20 19:11 d4h0

I mean that part mostly comes for free from the nature of reactive system. There isn't a single update function that runs over and over. If I were to write this by hand (ie what the Solid JSX compiler outputs) and compare it to stage0 it would be fairly similar except all the update points (ie attribute assignments) and reconcile calls would be wrapped individually subscribed to their data dependencies. And then instead of calling update yourself when data changes the reactive change would look at each atom that changed an only call the specific code pieces. The original function never runs more than once.

Now the overhead of creating these reactive subscriptions is what stage0 avoids and is why it is so fast on creation. It's very what you see is what you get. And as long as data isn't particularly nested on dependencies you can just manually call the update function at the appropriate level and your hand written guards will protect it from traversing down too far. Solid on the other hand already identified at compile time what can update on a specific data change and runs it. It has already flattened the updates into a list. So theoretically update performance can be better at the cost of creation performance. It's a memory for processing trade off. This is my most detailed example of how this works: https://indepth.dev/solidjs-reactivity-to-rendering. But it also requires a good understanding of reactivity at first.

Honestly if you are comfortable with stage0 type approach then you might have your answer. As you said it would be probably much easier to port. I'm pretty sure I could be pretty efficient with stage0 but thats because it has the same building blocks I use to build frameworks. I'm not sure most people know that clearing nodes with textContent = "" is the most performant way or updating the data property on textNodes themselves, or the difference between el.style.cssText, el.style.setProperty or directly assigning properties, or the mechanisms behind do event delegation, or what the difference is between the 3 different reconcile functions. When you sign up for higher level abstractions those sort of things are just handled for you.

But the truth of the matter is none of this stuff really matters that much. And I spend more of my time worrying about even higher level abstractions than what I'm talking about. It's really just a different scope. I did the TodoMVC app in an idiomatic way where I didn't put the edit state in the global store which leads to a bunch of extra mapping that is probably a pain to follow maybe I will refactor it. I could probably chop that down 20% or so using only local state. It comes down to the type of things you are developing. I'm confident using Solid or Svelte would lead you to writing less code, but that also isn't everything.

EDIT: Just streamlined it like I mentioned: https://github.com/ryansolid/solid-todomvc/blob/master/src/index.jsx

ryansolid avatar Nov 12 '20 22:11 ryansolid

Very interesting! Thanks again, @ryansolid!

Porting or creating a new library is probably not the smartest thing to do, but in this case it seems pretty interesting to me. In the past I probably never would have thought I could do this, but after programming in Rust for the last 9 months, I feel this is within my grasp (after Rust, most programming stuff seems much easier... 😅).

I actually found stage0 while researching a way to create a ReasonML binding for Solid, and I believe I found a way that isn't too difficult. In the next few days I will post my findings in the respective issue in Solid's repo. Maybe gaku-sei is interested in implementing it (I unfortunately don't have any time within the next 1-3 months).

EDIT: Just streamlined it like I mentioned: https://github.com/ryansolid/solid-todomvc/blob/master/src/index.jsx

Nice! This definitely is significantly shorter now.

That being said, the "difficulty in comprehension" I observed earlier still exists. I think the reason is the higher amount of abstractions in Solid (stage0 is almost vanilla JS). But, as mentioned above, this most likely goes away when these abstractions are used more often. This was just the first time I observed such a difference between two libraries, so I thought it made sense to mention it.

This is my most detailed example of how this works: https://indepth.dev/solidjs-reactivity-to-rendering. But it also requires a good understanding of reactivity at first.

This article looks very interesting! I definitely will read it as soon as I have some free time!

d4h0 avatar Nov 13 '20 21:11 d4h0