Conversation
|
as a user, *though there isn't really an explanation for that concept to begin with. i'm guessing that it's like the difference between new symbols (private) and registered symbols (public) in javascript? |
|
At first glance, I'm with @cAttte here. Could you expand on the motivation for the new API surface? |
From the discussion in #2425, it seemed to me like I'm not 100% satisfied with the way I implemented If we go down that route, maybe the API should just be On the other hand, that'd go back to being a pretty small change and wouldn't really address users' confusion over why there has to be a string key in addition to giving it a variable name. |
|
That just gave me another idea: What about doing it like I just described, but also making At least I assume that's most usages, maybe the actual data contradicts me and this doesn't make much sense... |
i'm sorry, but you still haven't explained what that is or why a user would want that! |
|
Think of it as a combination of both the anonymous, span-based keys and the string keys we have now. They are "private" because creating a state with the same string key but at a different source code location will always produce a different state. |
|
regarding the idea of using the state's initial value as a component of the key/hash: it makes sense when we think of the typical "static state" use case, where you have something like but the whole point of still allowing string keys is to aid in the "dynamic state" use case: here a user may have something like this is why i think it's better to force the user to go "all or nothing": either provide no key, which should break every time (here is where the compiler hint comes into play), or provide a key, which should never break (assuming reasonable keys). all of this, of course, on top of the new span-based key approach, as spans don't pose this problem. |
|
regarding private keys: i think i understand now. a private key is a span+string combo (which in my opinion is the ideal solution), while a public key is a plain string that universally identifies this state, in the same way that i think public keys are really bad and shouldn't exist. if you wanna share state between modules, just pass the |
Yes, exactly.
I feel like there might be some situations where it could be useful, but I also can't come up with anything concrete. Either way, it might be good to keep it temporarily (maybe as deprecated if people agree that it's bad) to give users time to update. |
|
just realized,
1. #let counter(key?)
2. #let counter(target?, key: none)
3. #let counter(target: none, key: none)the simplest option, #1, is ugly. #3 sounded very nice at first, but it incurs a wide-reaching breaking change: every cute |
|
I don't understand your issue with number 1, that seems perfectly reasonable to me. |
|
Just the counter could be enough. Just letting it be |
reified "private keys" are still a bad idea, even if less bad than bare string identifiers: they add an unnecessary layer of indirection. i agree that if you wanna refer to the counter, just refer to the counter. (not to a "pointer" to the counter).
(thanks for the #5716 link!) the symmetry is lost between
which leads me to this: i don't understand why we're still talking about counter keys. a user should just be able to do i guess that helps me understand the use case for public keys: it's more ergonomic to do |
|
I think at least having a private key type is actually a technical necessity, since |
|
I reworked everything based on the feedback here. Similarly, counter keys are now also private and you can call I'm not sure if I used all the special typst documentation syntax correctly, so some feedback on that would be appreciated. |
|
I think this isn't working as expected anymore. This: #let a = state("hi", none)
#let b = state("hi", none)
#a.update("Hello")
#context b.get()gives I think it's because the This is not the main obstacle though. From my point of view, there's two things:
|
The whole point of this PR is already breaking, so if people will have to look into how Note that the argument order of |
|
I disagree that the point of this is to be breaking. The point is to enable anonymous states. It might be that we come to the conclusion that the interface we want is breaking and then we have to gauge the impact and see how to deal with that, but there also certainly possible design that are not breaking. |
|
I meant "the point is breaking" as in "the point (making state keys anonymous) is breaking", not that being breaking is literally the point. But alright, I can see how it wouldn't have to be breaking. |
|
My concern is more breakage of packages than a good use case of the old behaviour. I'm not sure how widespread breakage would be, but I could imagine that a lot of packages stop working immediately if we aren't careful. I'd rather have those be warnings instead initially. |
|
How about the following: I can change this code to still store spans with the keys like now, but not actually use them yet, other than emitting a warning when the same key is used with a different span. Then, once that's released, we change it for the next version to actually use the spans for the keys. That'd be the most careful, non-breaking thing that comes to mind right now. |
|
I haven't made up my mind yet on how to proceed here. Unfortunately, I can't prioritize this now. Let's revisit this after 0.14. |
|
Currently, I'm not confident in this change and, for now, I'd rather not make it. I expect some change to happen at some point, but it will need some more time. I expect that this topic will resurface in relation to custom types where similar span-related things might be needed for type equality. This will hopefully allow me to make up my mind and make things consistent. It might also be a good opportunity to batch up breaking changes with appropriate migration paths and/or behavior. |
See #2425 for context.
I don't expect anything out of this PR, I just had some time and wanted to try implementing it, tho I'd also be happy to act on feedback and get this merged.
Implementation
[EDIT: See here for the new design.]
Most notably is the new type
state.key:The combination
state.key(none, public: true)is forbidden.The value for
public: autoistruewhenkeyis astrandfalsewhen it isnone.There's also a method
state.key.as-strthat returns the inner string, but only if the key is public.The constructor for
statehas been changed toSo the previous
state(s, init)is nowstate(init, key: s)and it is actually just a shorthand forstate(init, key: state.key(s)).The new
state(init)without a key is actually a bit special and not equivalent tostate(init, key: state.key(none))(see below).Anonymous Keys
States with anonymous keys come in three flavors:
state(init, key: state.key(none)): This is the most basic idea of an anonymous key and just uses the span of thestate.keycall as the key.state(init, key: state.key(s, public: false)): This uses both the span of thestate.keycall and the stringsas the state key, allowing the use of anonymous keys in loops and such.state(init): This is a sort of compromise between the two previous ones: It uses both the span of thestatecall and the hash ofinitas the key. My reasoning for this is that states with the same key and different initial values are rarely if ever useful and this is a sort of compromise to make something like Anonymous states #2425 (comment) work (at least as long as the arguments tocreate_stateare different -- two separate calls with the same argument would still create the same state).Other Implementation Notes
I'd like it to be
state.key()instead ofstate.key(none), but I couldn't find a way to make this work without having to manually parse the arguments, which I'd like to avoid to keep using the auto-generated arg parsing errors.Also, I haven't written any tests for now, but I'd be happy to do so if we decide to merge this.