-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rename $derived.call
to $derived.lazy
#10334
Comments
To me this API implies that the |
|
This is actually more of a problem than it may look (other that being confusing): Maybe $derived.invoke could work? |
Other ideas that have come up — The main reason we didn't go with $derived.from(observable);
$derived.from(store);
$derived.from(async_iterable); But as soon as you have one Another naming consideration is how we talk about these things in the abstract. One thing I particularly like about |
If one of the answers is no, i don't think $derived.lazy can be the right choice. |
I do not like this name; Still, the name is odd if this is just different syntax and everything is lazy by default. |
I honestly think the original name — On the other hand, I'd rather it be |
The opposite is true actually. We don't evaluate them immediately ever, and only lazily invoke them when needed. We don't need to know about their dependencies until an effect or another derived needs to know about them. |
Things I don't love about
|
I strongly agree. In that case, two options come to my mind:
|
let a = $derived(expression); // evaluated eagerly
let b = $derived.lazy(() => expression); // evaluated lazily I don't understand I suppose it's been discussed but couldn't find it in the other thread, what's the technical limitation to just have let a = $derived(expression);
let b = $derived(() => expression); |
If I was king of programming, overloads would be illegal. The cases where they're appropriate are vanishingly rare; they almost always end up being a source of regret. Aside from being inherently confusing, a practical issue is that the derived value is often itself a function: const filters = {
all: () => true,
done: (todo) => todo.done,
pending: (todo) => !todo.done
};
let filter = $state('all');
let filter_fn = $derived(filters[filter]); This won't work, because Svelte will try to set -let filter_fn = $derived(filters[filter]);
+let filter_fn = $derived(() => filters[filter]); ...which means you have to know to do that, which means having a deeper-than-should-be-necessary understanding of the underlying mechanics. It's just bad API design. I'd be very slightly less opposed to it if we could say that the distinction is based purely on whether you pass a function expression rather than any function value, but there's no way to make that work with TypeScript. |
What about $derived.explicit? |
I suppose there's no reason it couldn't just be I do want to defend the
Though they are more or less synonymous, I don't think the familiarity for people coming from those frameworks should be discounted, given that
|
What about something like |
|
What about |
Puruvjdev originally suggested a property on $derived, called 'function'; I suggested the universally understood shorthand 'fn'. I still don't understand the aversion to the shorthand 🤷♀️. If the argument is that you'd like to keep valid-English names in Svelte, there's 'props' (a CS specific shorthand for properties?). Edit: $derived.use would be in the spirit of Svelte re-using named concepts 😅 |
But there isn't a case where you don't immediately notice something is wrong, error or not, right? And the problem is strictly the same with the (unknown to the user) existence of a dedicated method? What if I expect this behavior to happen for whatever reason? If I were to be stuck in this situation my first instinct would be to wrap it in a function, because that sounds worth a try, is simple and quick enough to try, and many frameworks use this in pseudo-similar cases. And that's general JS behavior too. You have a built-in tutorial right here, simple, intuitive and easy to remember. In any other case I am doomed to scroll through the docs in quest for a method that will not be clearly named whatever it is, and that I will have to recall and call, and also adapt my code if I switch from one to the other, and most likely wonder why Svelte compiler can't do this work for me. That might be (arguably) better API design but that a way worse DX here. The struggle to find the right naming in this thread and the initial one is because there is conceptually nothing to name. |
IMHO the similarity between
Yeah, we have
The emphasis is on the wrong word! It should be on the something. An error like 'true is not a function' is only so helpful when you're trying to figure out what happened.
...no? I'm not sure what you mean here. Without an overload, the behaviour is consistent and predictable — even if I don't know about the variant, I can always do
That's just not true. You'll learn about |
By the way, I'll grant that there are convincing arguments here against
etc. I think @danielzsh's suggestion of 'complex' is probably closest, though it seems less like a description of the thing itself than a description of a frequently co-occurring property of the thing |
What about |
|
Plus the error case is highly specific to some constructs like this one (not used by beginners), else it just works or worse case you just got a non-working reactivity like you already can today in many ways.
But.. I have to know this and also have to come to a point where it doesn't behave like expected first? So strictly the same situation?
Which screams your own words "Here's own to do a thing, but wait, don't forget to scroll down and learn this other thing else you will get stuck at some point". That's truly terrible, half of the readers won't go that far and those who will will wonder why this can't be done automatically. Brains don't like to pick what they deem superfluous. So now not only you have to go through the docs (I can't just say to my teammate "use the $derived rune") but most likely you have to do it twice!
The amount of people under-using or not using at all autocomplete is extremely high, don't count on autocomplete for anything. The tremendous struggle to name this thing isn't there randomly. |
Apparently I'm in the minority, but I really like |
I can't help but hearing/seeing "gogoGadget.spelledOut, gogoGadget.byCalling, ... " |
Other suggestions:
|
It won't. The |
|
Perhaps I didn’t read the RFC close enough, just took a glance. I thought it would be a syntactic sugar for functions. |
One more suggestion that is more in line with what Rich-Harris was asking:
|
Probably confusing if $derived.expressive is the one that doesn't take an expression |
what if is there anything there? for what it's worth, when i first saw the EDIT: i only thought it was odd because the |
It is an expression in the first place because it's short and convenient. const z = $derived.expression(x + y);
const z = $derived(() => x + y); |
Though the same could be said for the whole topic, compare: const z = $derived.anything(() => x + y);
const z = $derived(() => x + y); |
TBH, I don't understand why they introduced this rune at all. If you need this functionality, you can use a IIFE. const z = $derived.anything(() => x + y);
const z = $derived((() => x + y)()); Or - if you want it a bit cleaner - use a normal function. function whatever() {}
const z = $derived(whatever()); |
Make the runes API simple:
In practice this means adding Before: let doubled = $derived(count * 2); After: let doubled = $derived(() => count * 2); EDIT: Another advantage is that This reminds me of inline events handler. In svelte it's normal to do this (for simple expressions): <p on:click={() => { count++ }}>
<button on:click={() => count++}> And no one complains that it could be simplified to this: <p on:click={count++}> |
What should or shouldn't accept |
@paulovieira we debated that option at length and concluded it was the wrong choice |
In my opinion, the need to return a function from What if
|
@iamim it also already was discussed: #10240 (comment) |
Yeah, the same arguments against overloads apply with interest to 'overload but also another thing'. That would be needlessly chaotic. We're not going to find a good descriptive noun-ish word for this (and After conversing with other maintainers, the most logical option is foo = $derived.by(bar); ... |
Although Average user :
I think this thread is needlessly chaotic. A simple and intuitive syntax that would work exactly as expected for the vast majority of users, would require a couple of seconds of thinking for the others and cause maybe 3 people to create an issue isn't. By the time it's an actual problem it won't be the only part of the syntax to break change anyway. I understand you want the new direction to be as robust as possible but perfect is the enemy of good. It feels like a replay of the first version of |
@gterras your examples of issues with a But if $derived is overloaded and handles functions differently than other values, then you won't notice anything is off until execution. It will be difficult to find what is going on and how to fix it (and the fix is ugly). My 2c on naming: I also think that I think that |
Yes that's pretty much the only raised case against this despite the |
What do you mean with on:eventname working the same (overloaded)? |
I mean the same thing as then you won't notice anything is off until execution :
No TS error in this case, you have to run it to realize something's wrong. It probably happened to most people trying Svelte for the first time but was never considered a problem, and the solution is quite intuitive. |
I do get an error "Type void is not assignable to type 'MouseEventHandler | null | undefined" when I try to write that myself Still, the event handler requiring a function is indeed quite intuitive (it is the same in vanilla JavaScript), but for $derived having an overloaded type like the following is not intuitive at all: declare function $derived<T>(expr: T): T
declare function $derived<T>(func: () => T): T Especially in cases where "T" would be a function like |
That is never ideal of course, but there's a big difference IMO when something always works in a certain way, and you're using it the way you always do, but suddenly it behaves differently and there is no error / warning. |
|
if |
It took me a long time to understand my own negative reaction to A filler word like Thank you everyone for the spirited conversation and for setting me straight on |
To me, |
Describe the problem
In #10240 we added
$derived.call(fn)
alongside$derived(expression)
, and somehow overlooked the fact thatFunction.prototype.call
is a thing.Strictly speaking this is fine — these are runes, not functions, and we can do what the hell we want. But realistically this is going to be a source of confusion at best and annoyance at worst for many people.
Describe the proposed solution
Programmers often talk about 'lazy evaluation', which basically means providing a function to compute some value rather than the value itself.
Even though the under-the-hood mechanism of
$derived
is that it's always lazy, meaning that this......is syntactic sugar for this...
...I think the conceptual framing is the right one. I therefore propose
$derived.lazy
:We'll also need to add an error message for anyone who is already using
$derived.call
, of course.Importance
would make my life easier
The text was updated successfully, but these errors were encountered: