Hacker News new | ask | show | jobs
by rich_harris 996 days ago
> it looks like it’d be needing to do quite a lot of control flow analysis

Implementation-wise, this is vastly simpler than Svelte 4, because everything is explicit.

One thing we didn't really show today is how this works in your editor in practice — for example, TypeScript thinks `$state` and `$derived` are just the identity function. It makes sense when you use it, but I appreciate that I'm basically asking you to just trust me on that!

> Previously, Svelte’s reactivity model was very easy to understand, including its limitations, because it was the result of very simple analysis

I totally understand what you're saying. But in fact the analysis is _anything but_ simple — in places it's frighteningly complicated, to the point that even I sometimes don't understand what's going on! And that's part of the problem — because Svelte 4 is more implicit, you can't really know what's happening in a lot of cases, you just have to hope that the compiler knows what it's doing.

3 comments

I've seen the demo and you're right, in cases where the code gets more complex, it does look way cleaner. I also really appreciate that you guys made it so that you can still use Svelte in the same old way.

But at the same time I just wish there was some other solution to this while keeping it implicit somehow. I would do anything to keep things implicit while solving the backend complexity some other way. The thing I most love about Svelte is the principle / vector of "as little learning as possible", as well as in many cases transfer of learning, makes it much more user friendly, and I wish evolutions of Svelte continued to evolve along that direction, by reducing more and more.

But things like `$something` are a bit strange. Even `$:` is strange. It adds cognitive load. It's not very English-like and in turn not easily parseable. `on:click` is closer to the attractor of user friendliness. I think all programming languages and frameworks should try to approach English or Python, to allow for maximum "transfer of learning" so things just feel like they flow "without thought". Taking inspiration from UI/UX... things should be as close as possible to 'understanding at a glance'

The gold standard would be to approach something like this level of intuitiveness in the future as crazy as it may seem: https://twitter.com/brianjoseff/status/1617556877218570241 https://twitter.com/mathemagic1an/status/1700232760756207956...

I have many ideas on how, but it would no longer be a programming language.

> for example, TypeScript thinks `$state` and `$derived` are just the identity function

That seems like a missed opportunity on Svelte’s part… but hard to fault, because TypeScript doesn’t support nominal primitive types very well. Ideally it would be something like Reactive<T> to signal (ha) that it’s not just a plain value.

We've toyed with this idea. There's a couple of problems though. Firstly, if you have this...

type Reactive<T> = T; function $state<T>(value: T): Reactive<T>

...then TypeScript will 'unwrap' the type anyway, unless you do funky stuff like this...

type Reactive<T> = T & { [uniquesymbol]: any };

...in which case things like `value += 1` will cause type errors because it coercies `value` from `Reactive<number>` to `number`.

But it also creates problems here:

let message = $state('hello'); obj = { message };

The type of `obj.message` is Reactive<string>, but it's _not_ reactive — it's a non-reactive snapshot of the value when the object was created.

It's possible that we can do some fun stuff with TypeScript plugins, but we haven't dived too deeply into it yet.

> TypeScript will 'unwrap' the type anyway, unless you do funky stuff like this […] in which case things like `value += 1` will cause type errors because it coercies `value` from `Reactive<number>` to `number`.

Yeah. I’ve spent more time than I’d like to admit trying to find a nice solution to this, and I’ve ultimately arrived at “it needs type system support to work for the general case”. I think you can make your brand (symbol) optional to address this case, but it doesn’t address this:

> The type of `obj.message` is Reactive<string>, but it's _not_ reactive — it's a non-reactive snapshot of the value when the object was created.

And here is the real problem: the types are right (in this case)! It’s Svelte that is wrong (in that it deviates from the JavaScript language semantics, which TypeScript has correctly modeled).

This leads to my other ultimate conclusion with nominal primitive types: most of the time you’re better off just boxing the value… unless, or until, it becomes problematic for other reasons (eg performance, which would probably be a concern here, albeit one worth testing). Boxing allows correct, refined typing; it makes semantics of the value clear. The only downside is convenience… but this sort of convenience is exactly the kind of thing people rightly judge about the JS ecosystem.

I’m not likely to use Svelte (for a variety of other reasons), so I don’t have a particular dog in this race… but I will say I find more explicit types for signals a great deal nicer to work with. Whether the explicit mechanism is a function call (like Solid) or an accessor (like many others).

With Rust, mutable variables are underlined by RustAnalyzer by default - it would be really useful for reactive variables in Svelte to have a similar distinction.
Fair enough. I’ll see how things go and wish you well. For simple cases, the new will be syntactically-inferior, but it makes sense that the explicitness allows it to be more consistent. Ah, modelling mutable trees as stores… not a great deal of fun.