Skip to content
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

TemporalQuery.queryFrom should return @Nullable R #125

Open
chaoren opened this issue Feb 19, 2025 · 9 comments
Open

TemporalQuery.queryFrom should return @Nullable R #125

chaoren opened this issue Feb 19, 2025 · 9 comments

Comments

@chaoren
Copy link

chaoren commented Feb 19, 2025

The documentation says "may return null to indicate not found":

* @return the queried value, may return null to indicate not found

Also, maybe R shouldn't extends @Nullable Object:

public interface TemporalQuery<R extends @Nullable Object> {

since the meaning of a null return value would be ambiguous if R itself is @Nullable.

@cpovirk
Copy link
Collaborator

cpovirk commented Feb 19, 2025

The understanding I came to is that there are "TemporalQuery instances that can return null" and "TemporalQuery instances that cannot return null," in much the same way as there are Function instances that can return null and others that can't. (I base this on the constants in TemporalQueries.) That would make it useful to represent that distinction in the type system—TemporaryQuery<Foo> vs. TemporaryQuery<@Nullable Foo>. I agree that the Javadoc for TemporalQuery itself does not even hint at this, but does that sound right in light of the TemporalQueries class?

@chaoren
Copy link
Author

chaoren commented Feb 19, 2025

does that sound right in light of the TemporalQueries class?

It seems every single type argument to TemporalQuery in TemporalQueries is @Nullable.

"TemporalQuery instances that cannot return null,"

Where do you see these?

@chaoren
Copy link
Author

chaoren commented Feb 19, 2025

TemporalAccessor.query(TemporalQuery) also says "null may be returned (defined by the query)":

* @return the query result, null may be returned (defined by the query)
* @throws DateTimeException if unable to query
* @throws ArithmeticException if numeric overflow occurs
*/
default <R extends @Nullable Object> R query(TemporalQuery<R> query) {

@cpovirk
Copy link
Collaborator

cpovirk commented Feb 19, 2025

...huh :) I may have gotten confused when I saw a few entries like "A query for LocalDate returning null if not found" and then a few entries without "returning null if not found." And then presumably I didn't reevaluate when I actually looked at all the methods as part of annotating them.

Oh, maybe what I was actually going on was this...?

The most common implementations are method references, such as LocalDate::from and ZoneId::from.

And now I see your message about TemporalAccessor [edit: "defined by the query," which I read as supportive of the idea that the nullness is a property of the instance's type]. Maybe that?

But I'd need to look into how instances are actually used in order to understand what that's about....

OK, here are a couple factory methods (which I won't pretend that I have any memory of) that do appear to be claiming that they always return non-null values:

@chaoren
Copy link
Author

chaoren commented Feb 19, 2025

Some implementations of TemporalAccessor.query(TemporalQuery) (including the default implementation) actually explicitly return null. I'd take that as a signal that it should be returning @Nullable R instead of R. Are we running the nullness analyzer on the annotated JDK itself? It would be complaining about the explicit return nulls without a @Nullable R return type, right?

@cpovirk
Copy link
Collaborator

cpovirk commented Feb 19, 2025

We don't run nullness checked on the annotated JDK. (We also haven't made much effort to annotate anything beyond publicly visible APIs, though we're inconsistent about that.) I agree that a checker would produce an error for...

default <R extends @Nullable Object> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.zoneId()
|| query == TemporalQueries.chronology()
|| query == TemporalQueries.precision()) {
return null;

...but I think that's one of those very rare cases (like in AbstractFuture) in which the code is correct and we'd just need to suppress: The default implementation directly returns null only for cases in which the given query is one of the TemporalQuery<@Nullable Foo> instances.

@chaoren
Copy link
Author

chaoren commented Feb 19, 2025

Documentation on DateTimeFormatter.parsedExcessDays actually seems to imply that returning null would be expected, but they're doing something unusual. There's no reason to say "with a zero period returned instead of null" if you're not expecting it to return null in the first place.

* access to additional information from the parse. The query always returns
* a non-null period, with a zero period returned instead of null.

@cpovirk
Copy link
Collaborator

cpovirk commented Feb 19, 2025

As for how these things are even used: It looks like a large fraction of the usages of TemporyQuery in our depot involve parsing, either formatter.parse(text).query(query) or the (equivalent, I hope?) formatter.parse(text, query). In that case, we very much want for formatter.parse(text, LocalDate::from) to return a non-null LocalDate.

(And now I'm confused as to how formatter.parse(text, query) could promise to return "the parsed date-time, not null" if callers can pass a TemporalQuery that can return null....)

@cpovirk
Copy link
Collaborator

cpovirk commented Feb 19, 2025

(And now I'm confused as to how formatter.parse(text, query) could promise to return "the parsed date-time, not null" if callers can pass a TemporalQuery that can return null....)

(Answer: The doc is wrong: ISO_DATE.parse("2025-02-19", zone())) returns null :))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants