-
Notifications
You must be signed in to change notification settings - Fork 292
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
cue: Path APIs are verbose and clunky for common scenarios #3159
Comments
Note that if we were to go down the Proposal 1 route, which is my preference, we would need to delete the existing Alternatively, since existing valid calls like |
Apologies: I didn't read your suggestion closely enough. So with proposal 1 we would end up with these two methods, correct?
|
Correct. The signature for LookupPath is fine for cases where one does want to make use of a parsed or long-lived path, but for most cases, I want to use an "inline" path that I don't want to keep or reuse, hence the Lookup signature with |
Paul's comment of the original intent still stands. We first need a way to remove
I don't think this is nearly as ambiguous. The word
Paths can be return values as well, in which case having to piece them out as selectors is tedious. Personally, I think it would be even nicer to have a To avoid solving the transition issue now, the signature could be The body would be:
(In practice this would use the lower-level lookup methods within the loop, of course.) This allows neat things, like combing parsed and non-parsed component:
Note that this variant of the proposal would allow deprecating |
I'm fine with whatever API choice we make as long as the common scenarios become simple again. I did not originally suggest a func taking parameters like I agree that the first step here is likely to fully phase out the existing Another transition option is to swap I would personally advocate for such a "swap" transition because, if we can pull it off without breaking most users, it would be far less painful for them than having to switch methods for every single one of their calls, even the trivial ones like |
Ah. Missed that. I think that phasing out within the current API is not quite an option. I am not a fan of breaking API backward compatibility in such a hard way, even if we do not have a stability guarantee yet. We have a lot to clean up, so starting a v2 where things are done right makes more sense.
However appealing that would be, it is also weird and dangerous. Consider the following code:
It is legitimate to pass I agree with you that the "swap" transition is nice, as I also think it will prevents breaking changes for the most part. I think, with good documentation, we can make it clear what it means. Also, we get to try if this is a viable API before fixing this in a v2 of the API. Once we have this, we could also consider a |
Pleasantly surprised that we agree on this :) I just worry about forcing nearly every Go API user to rewrite
For what it's worth, I prefer to tackle these issues as if v2 were not an option, to see what we can do in a matter of weeks and not years. v2, or even just stabilizing as a v1, is a big step that will force us to rethink core parts of the API like Context and Value. I don't think we need to hold up smaller bits of UX polish until v2 unless we absolutely must. We can certainly leave "cleanup TODOs" as part of this design though, such as for example removing
There is no such "customizable |
Background
https://cue-review.googlesource.com/c/cue/+/7441 introduced the concept of the
cue.Path
type, which is made up of a sequence of selectors. Notably, a selector is typed, and can be one of multiple specific implementations, like:cue.Str
for label strings (identifiers)cue.Def
for definition labelscue.Index
for list indicesNew APIs were introduced that use paths, like:
This is in contrast to the old and deprecated APIs such as:
The commit message above explains why this API was problematic:
val.Lookup("3")
do? Should it be equivalent tocue.Str("3")
, treating it as a label string, orcue.Index(3)
, treating it as a list index?For the two reasons above, I think this was a good change to make in terms of the base API. However, it did make many common use cases significantly more verbose. For example, from a recent selection in https://review.gerrithub.io/c/cue-lang/cue/+/1194951, we can see that
became the much longer and more complex
Alternative:
cue.ParsePath
One alternative is using
cue.ParsePath
as follows:Which is simpler and shorter, but as a parse API, it can fail on invalid syntax, and it is noticeably more complex and expensive. I think parsing paths should be used primarily when the path is an arbitrary input string, such as given via the command line or as a URL parameter, and not when the path itself is fairly static in the code.
I also think path parsing suffers from similar pitfalls mentioned before; what should
cue.ParsePath("3")
do?Proposal 1:
Lookup(...Selector)
In my limited experience with the "path" APIs so far, I've come to realise that in most cases, I don't care about the path value. Nearly every time I am making a path, it only exists so I can use it immediately after and throw it away. In a few times I might make a path and reuse it multiple times, in which case calling the "make" API is fine, but that's usually not the case.
In that spirit, I suggest that we add "inline path" APIs which do the
cue.MakePath
logic by themselves, reusing the deprecated and hidden APIs likeValue.Lookup
:This way, our earlier example could be rewritten as:
I also imagine it could avoid "making" the path entirely, instead using the selectors directly, avoiding any allocations for the path itself.
Note that this solution still keeps the new
cue.Str("info")
instead of the older"info"
, due to the problems already listed with the original API.Note that this solution takes
...Selector
rather thanSelector
as a parameter, so that the example above doesn't have to do two lookups in sequence:Finally, note that
MakePath
would remain in place, for the case where one wants to make a path and reuse it many times with e.g.Value.LookupPath
, perhaps even as a global variable.ParsePath
would also remain in place.Proposal 2:
LookupStr(string)
A variation of the proposal above is to provide single-selector method shortcuts for each kind of selector, or at least for the common ones. For example,
v.LookupStr("foo")
would be short forv.LookupPath(cue.MakePath(cue.Str("foo")))
, and our example from earlier would be:I personally lean against this variant of the proposal, as it would multiply the number of methods needed for shortcuts:
LookupStr
,LookupDef
,LookupIndex
, etc.The text was updated successfully, but these errors were encountered: