-
Hi! Is there a deeper reason behind separating the setter from the tracking getter, or was it to avoid cycles when getting and setting in the same call? Looks a bit wasteful, never calling the getter with any value. But I'll take your sampling/untracking API for a spin, looks clean.
vs
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
It's mostly to enforce unidirectional flow. Which while might seem like it is to guard infinite loops is more than about avoiding the worst case. I don't think the default should be that passing the signal also passes around the ability to write to it. It's about keeping it known explicitly when we are mutating. It also tends to make it more burdensome and explicit to allow other places to update them. There is this sort of ambiguity when you get a thunk and am like can I pass an argument to it and set it? Or better, should I be able to set it? Thinks like async derivations where you can't use the synchronous primitive so you use the signal but then something else could write to it. Sure you can create these guards yourself but it's never clear what you are dealing with. There is also this sort of inconsistency that presents when using them as thunks when they may have arguments. Shortcut pass by function type scenarios can lead to unexpected results if that function ever does pass an argument. What is nice here is it is consistent.. Signal, Memo, Resource, State thunk, all things that read look like It's a lot like the concept of lenses in FP. It abstracts the internals and provides a generic interface. Composition tends to follow the same patterns: function createDoubleSignal(init) {
const [s, set] = createSignal(init)
return [s, (v) => set(v * 2)]
}
const [s, set] = createDoubleSignal(); This is a pretty silly example and you could do it the other way: const s = createSignal(init);
return (v) => v !== undefined ? s(v * 2) : s(); But it scales beyond this. Once you start writing this way you will see how most thing are sort of patching these read/write cycles. It might seem different at first but it is very much a philosophical thing that I took into designing Solid and talk about it a bunch in a number of my articles. I think unchecked assignment in reactive systems are their biggest weakness. And while I'm not so strict to enforce single assignment this is the least we can do to encourage good patterns and reduce footguns. Oh and last but not least it looks like React Hooks which is simple win in itself. |
Beta Was this translation helpful? Give feedback.
-
The composition reminded me of https://github.com/day8/re-frame Which I tried to reimplement with S.js a long time ago, with lots of footgunnery. Seems much closer to Solid than React is, apart from JSX. But that obviously doesn't help, as it's rather obscure 😅 Thanks for the thoughtful explanation! |
Beta Was this translation helpful? Give feedback.
It's mostly to enforce unidirectional flow. Which while might seem like it is to guard infinite loops is more than about avoiding the worst case.
I don't think the default should be that passing the signal also passes around the ability to write to it. It's about keeping it known explicitly when we are mutating. It also tends to make it more burdensome and explicit to allow other places to update them.
There is this sort of ambiguity when you get a thunk and am like can I pass an argument to it and set it? Or better, should I be able to set it? Thinks like async derivations where you can't use the synchronous primitive so you use the signal but then something else could write to it. Sure …