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

Optional<T> should be Optional<T extends Object> #666

Open
cbracken opened this issue Nov 5, 2020 · 8 comments
Open

Optional<T> should be Optional<T extends Object> #666

cbracken opened this issue Nov 5, 2020 · 8 comments
Labels

Comments

@cbracken
Copy link
Member

cbracken commented Nov 5, 2020

Optional is intended as a substitute for non-nullable values. Ideally, it should have a generic type of T extends Object in order to prevent non-sensical declarations like Optional<Foo?>. Such declarations are unlikely to happen intentionally, but one could imagine the case of an Optional used in another generic class, where T is a nullable type.

Ideally, we should prevent such uses by declaring Optional as Optional<T extends Object>.

This is a potentially significant breaking change, since currently T implicitly extends dynamic and therefore checks like the following don't produce any analysis errors:

if (obj is Optional) {
  return obj.value.someMethod();
}

where someMethod is a method defined on T. All such call sites would need to be fixed up to obj is Optional<AppropriateType>.

A survey of the Google codebase suggests this would be a fairly extensive breaking change. Given that Optional makes little sense once null-safety is enabled in the language by default, the simpler change may simply be to leave as-is and deprecate the class once null-safety lands in the language.

@cbracken cbracken added the nnbd label Nov 5, 2020
@cbracken
Copy link
Member Author

cbracken commented Nov 5, 2020

/cc @yjbanov and @davidmorgan for thoughts.

cbracken added a commit to cbracken/quiver-dart that referenced this issue Nov 5, 2020
Optional is intended as a substitute for non-nullable values. Ideally,
it should have a generic type of `T extends Object` in order to prevent
non-sensical declarations like `Optional<Foo?>`. Such declarations are
unlikely to happen intentionally, but one could imagine the case of an
Optional<T> used in another generic class, where T is a nullable type.

In google#652, we prevented such uses by declaring Optional as
Optional<T extends Object>.

This turns out to be a significant breaking change, since previously, T
implicitly extended `dynamic` and therefore checks like the following
didn't produce any analysis errors:

    if (obj is Optional) {
      return obj.value.someMethod();
    }

where `someMethod` is a method defined on `T`.

A survey of the Google codebase suggests this would be a fairly
extensive breaking change. Given that Optional makes little sense once
null-safety is enabled in the language by default, the simpler change
may simply be to leave as-is and deprecate the class once null-safety
lands in the language.

It appears that there are no checks on the return type of `transform`,
and this seems significantly unlikely, therefore we leave that change in
place.

This partially reverts 28301f9.

Related: google#666
cbracken added a commit that referenced this issue Nov 5, 2020
Optional is intended as a substitute for non-nullable values. Ideally,
it should have a generic type of `T extends Object` in order to prevent
non-sensical declarations like `Optional<Foo?>`. Such declarations are
unlikely to happen intentionally, but one could imagine the case of an
Optional<T> used in another generic class, where T is a nullable type.

In #652, we prevented such uses by declaring Optional as
Optional<T extends Object>.

This turns out to be a significant breaking change, since previously, T
implicitly extended `dynamic` and therefore checks like the following
didn't produce any analysis errors:

    if (obj is Optional) {
      return obj.value.someMethod();
    }

where `someMethod` is a method defined on `T`.

A survey of the Google codebase suggests this would be a fairly
extensive breaking change. Given that Optional makes little sense once
null-safety is enabled in the language by default, the simpler change
may simply be to leave as-is and deprecate the class once null-safety
lands in the language.

It appears that there are no checks on the return type of `transform`,
and this seems significantly unlikely, therefore we leave that change in
place.

This partially reverts 28301f9.

Related: #666
cbracken added a commit that referenced this issue Nov 6, 2020
Optional is intended as a substitute for non-nullable values. Ideally,
it should have a generic type of `T extends Object` in order to prevent
non-sensical declarations like `Optional<Foo?>`. Such declarations are
unlikely to happen intentionally, but one could imagine the case of an
Optional<T> used in another generic class, where T is a nullable type.

In #652, we prevented such uses by declaring Optional as
Optional<T extends Object>.

This turns out to be a significant breaking change, since previously, T
implicitly extended `dynamic` and therefore checks like the following
didn't produce any analysis errors:

    if (obj is Optional) {
      return obj.value.someMethod();
    }

where `someMethod` is a method defined on `T`.

A survey of the Google codebase suggests this would be a fairly
extensive breaking change. Given that Optional makes little sense once
null-safety is enabled in the language by default, the simpler change
may simply be to leave as-is and deprecate the class once null-safety
lands in the language.

It appears that there are no checks on the return type of `transform`,
and this seems significantly unlikely, therefore we leave that change in
place.

This partially reverts 28301f9.

Related: #666
@davidmorgan
Copy link
Contributor

I think just making Optional deprecated makes the most sense. People using Optional are likely to be the same people interested in migrating to null safety :)

Thanks.

@cbracken
Copy link
Member Author

cbracken commented Nov 6, 2020

sgtm. That avoids a breaking change before (eventual) removal.

@cbracken
Copy link
Member Author

cbracken commented Nov 6, 2020

Given that Quiver 3.0 will have a minimum SDK constraint of 2.12.0, I'll deprecate it in that branch now.

@cbracken
Copy link
Member Author

cbracken commented Apr 4, 2021

Closing this since we’re deprecating Optional; no need to force people through two migrations here.

@cbracken cbracken closed this as completed Apr 4, 2021
@yjbanov
Copy link
Member

yjbanov commented Oct 14, 2021

I propose that we reopen this issue. Even though deprecating Optional and nudging users to move to null safety is the eventual goal, T extends Object is a useful stepping stone towards that goal. By making T explicitly non-nullable users will get a hint from the analyzer at the time they are migrating to null safety to make they type that they feed into Optional non-null. From then on, moving from Optional<T> to T? is mostly a mechanical change.

@yjbanov yjbanov reopened this Oct 14, 2021
@jakemac53
Copy link
Contributor

jakemac53 commented Nov 11, 2022

Fwiw I have been running into this a fair bit where the migration tool suggests a nullable type for Optional objects, which makes no sense :(.

Not sure if it is too breaking or what, but definitely seems wrong that T is not bounded to non-nullable Object.

@jakemac53
Copy link
Contributor

Another idea here is you could do a less breaking change, where of also still accepted a nullable value. So all that would be disallowed would be explicitly typing something nullable (Optional<Foo?>). That is always guaranteed to not do what people wanted anyways, so its worth having it be flagged imo.

And it would be worth cleaning up any such instances in google3 as well.

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

No branches or pull requests

4 participants