-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: encoding/json/jsontext: add constants for each Kind #71756
Comments
I am in favour of this change (with the Kind prefix), because these kinds of constants are autocompleted by gopls, thus it is easier to work with them. |
Personally, i would change the names to: - KindBeginObject Kind = '{'
- KindEndObject Kind = '}'
- KindBeginArray Kind = '['
- KindEndArray Kind = ']'
+ KindObjectBegin Kind = '{'
+ KindObjectEnd Kind = '}'
+ KindArrayBegin Kind = '['
+ KindArrayEnd Kind = ']' |
RFC 8259, section 2 uses the terms As an aside, the term "kind" is unique to the Go "jsontext" package, but we need a way to describe the kind of a token. The term "type" would be incorrect since that's used by the RFC to describe null, strings, numbers, booleans, objects, and arrays (i.e., all complete values). |
I don't think this is a fair comparison and trying to abstract lessons from that doesn't feel right for this situation.
Also on the "effort" front are we talking just about number of character to type? Code is read more than it's is written.
That would increase the representations rather than shrink them which seems to be the exact argument against making constants? As for a complete separate stdlib package |
Related Issues (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
If we want opaque(-ish) constants, we could define:
...although if we do that, I think the values should be integers ( Either way, I think we should define these constants:
I also agree with @nemith's comments regarding |
Yeah, I think that if we go with constants it should be using iota. |
https://golangci-lint.run/usage/linters/#exhaustive Now that being said the use of exhaustive switch statement outside of the stdlib is probably limited. Most cases you are going to look for a specific token and if not move on. |
We tried that in the past, and renumbering the constants was also a performance hit since it involved many unpredictable branches. We could use a 256B lookup table, but I'm hesitant about using LUTs to solve re-mapping as it's adding more pressure on the data cache. The current approach is fast since it's just grabbing the first byte of the token (and only needs to branch for numbers). |
If it were just |
Also for the record I didn't much care that there was no if dec.PeekKind() == '0' {
*val = jsontext.Value(nil)
} Iit look a significant amount of time to realize that With constants it's obvious/self-commenting. |
IMHO the readability argument wins the day and we should add constants for these. |
@dsnet I don't follow. What constants are you renumbering, and why? What perf is involved? I'm imagining the code doing something like this: func (e *Encoder) PeekKind() Kind {
switch e.token {
case "{": return KindObjectBegin
... I don't see how there's a perf difference there when KindObjectBegin is a custom byte value like |
Do you see a performance difference between:
and:
|
Don't you need to add an exception Kind? |
token[0] doesn't work for numbers, null, true, false, etc. |
What do you mean? It absolutely does work for |
I see what you mean about those. Still, it doesn't work for numbers. In any case, the code just becomes func (e *Encoder) PeekKind() Kind {
switch e.token[0] {
case "{": return KindObjectBegin
case "}": ...
...
default: return KindNumber
} I don't see how using iota for Kind* values makes that perform worse. |
func (e *Encoder) PeekCharKind() Kind {
tok := Kind(e.token[0])
if tok >= '1' && tok <= '9' {
return '0'
}
return tok // bug, I didn’t consider '-'
}
func (e *Encoder) PeekIotaKind() IotaKind {
switch e.token[0] {
case 'n': return IotaKindNull
case 'f': return IotaKindFalse
...
default: return IotaKindNumber
}
} Running off a loop of tokens, one of each possible starting character:
|
Since json/v2 would be a net-new stdlib addition (thus any decision we make now can't break anyone yet), and since |
Would |
If the constants are named it doesn't especially matter what the values assigned to each name are. |
Honestly, I think we’ll avoid the |
Nonetheless, we can add vet coverage for these, and not rely on adoption via path-of-least-resistance. |
Proposal Details
This is a sub-issue of the "encoding/json/v2" proposal (#71497).
Here, we discuss whether to add constants for each
Kind
.This builds on top of the v2 API and does not block the acceptance of v2.
Specifically, this issue would result in the following changes to the API:
The choice of using a
Kind
prefix matches thehttp.MethodGet
-like constants. We need at least a prefix or a suffix sinceNull
,False
,True
, etc. conflict with the existingToken
declarations.Analysis of this change:
0
vs'0'
). This problem could be alleviated bygovet
orstaticcheck
.http.MethodGet
literal and instead just use "GET". It is almost certain that the world will be inconsistent about usage. In the case of "jsontext", it is notably more effort to referencejsontext.KindNull
rather than use the'n'
literal.Alternatives
What about making
Kind
an opaque type?We could, but there was a performance hit since you are no longer performing a comparison against a constant or
constant literal. For example, let's suppose we had:
Opaque types cannot be declared as constants. Referencing a global variable requires referencing the memory location for that variable, which is slower.
What about a separate package?
It should be noted that this could also just be a third-party package:
which is shorter to reference as
jsonkind.Null
instead ofjsontext.KindNull
.The text was updated successfully, but these errors were encountered: