-
Notifications
You must be signed in to change notification settings - Fork 363
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
Definitely non-nullable types #268
Comments
My first impression is that the syntax feels clunky, and is very specific. Could this be tackled with a more general solution? Taking the original Java use-case: <T> void put(@NotNull T t); Could this be solved by introducing some sort of refined/non-parameter generic types? fun <T> put(t: TNotNull) where TNotNull : T, TNotNull : Any
// Needs to be defined somehow ^^^^^^^^ ^^^ "Type parameter cannot have any other bounds
// if it's bounded by another type parameter" Or allowing Using contracts also comes to mind, but that's a bit different semantically. It also (I think?) doesn't change the signature in the same way. |
I am not sure about the importance of the use case, but there is a problem with the syntax. Double bang ( Also, have you considered a more universal way - type intersections. |
@BenWoodworth Thanks for the suggestions!
Yes, we may just introduce (at least partially) intersection types for
Such a type parameter would make a signature for
We've always tried to avoid bringing Java-specific nullability annotations to Kotlin, since there's already a syntax for nullability. |
Thanks! That is a bit of dissonance we are afraid of. At the same time, one might have an intuition that it's not very surprising that expressions having a form of |
@dzharkov It is problematic from the teaching perspective. We are teaching that double-bang must be avoided and should be used only under very specific conditions. But introducing another similar syntax where recommendations are completely different, would bring a lot of confusion and significantly complicate the understanding. For new people, the similar syntax should have a similar meaning and similar usage practices if want it to keep being simple. |
I fully agree with @altavir.
Honestly, I like to understand the problem better, I don't feel it as a problem. The example |
@fvasco |
@altavir @fvasco This is not a contradiction to me. I would say "double-bang types" should also be avoided and only be used under very specific conditions. The KEEP states there are special conditions, where they are required for Java interoperability. The ugly double-bang would help guiding people to derive 'T' from non-nullable type and use 'T?' (as fvasco pointed out). |
If this proposal is implemented, we're going to relax some stdlib function signatures by removing |
Is there currently any difference at all between the following?
|
Yes, of course. Call |
If Kotlin had type intersections already, It would be much better to have just What if make // In both functions these statements would be correct
// [a] is definitely not nullable
// [b] is definitely not nullable
// [c] is nullable list of definitely not nullable elements
fun foo(a: Int, b: Int?, c: List<Int>?)
fun <T> bar(a: T, b: T?, c: List<T>?) Flexible types would still work as currently. As far as I can see. Second option is to change notation of flexible type What do you think? I did you discuss costs of breaking changes in this area? |
@dzharkov If there aren't any cases where this would be necessary outside of Java interop, it seems like it would be much simpler to add another JVM-specific annotation. I don't understand the concern of shipping it in the stdlib considering that the common namespace is already polluted with |
Thank you all for your suggestions! The main change is that we moved from confusing On the concern that it might be obscure for newcomers:
|
Changing notation for flexible types just because of a very rare feature looks too hard for me |
Agree, one might say it's already polluted, but we still do our best to avoid making it worth (at least for rarely used features) |
Is there a possibility maybe to put a (very rudimentary and rough) version of intersection types behind an experimental flag? Because IIRC intersection types are already in the compiler and so having a rough user-facing version of them behind a flag shouldn't be too difficult. Just a little something that we can experiment with for the time being |
I'm definitely hyped for intersection types! Syntax sugar like |
For the record, while I understand the underpinnings of the particular syntax chosen ( |
I agree, but is still better than T!! and plays well with hypothetic real intersection types in the future. |
Let me offer a use case that may motivate the ! as a complement to ? on types.
If you think about it, T::class.java isn't type Rather than being an assertion that T is not a nullable type, T! could 1) express that a type is the non-nullable version of a potentially nullable type, and 2) act as an operator on a reified type that outputs the non-nullable version. #2 would allow In this way, it would be an effective complement to T?. |
There seems to be only one use-case for this, implementing a Java interface like the one in the proposal: public interface JBox<T> { // I'm assuming putting the type parameter on the method was a typo
void put(@NotNull T t);
} This feature is only necessary for Kotlin/JVM, so it should be done with public interface Box<T : Any> {
fun put(t: T)
}
// Better example which uses both nullable and non-nullable in the signature. Imagine this function returns the previously held value, or null on the first call.
public interface Box<T : Any> {
fun set(t: T): T?
} @roxton Kotlin doesn't have a ternary operator fun interface Deserializer<T> {
fun deserialize(data: ByteArray?): T?
}
fun <T : Any> lookup(type: KClass<T>): Deserializer<T> = TODO()
fun <T> Deserializer<T>.optional(): Deserializer<T?> = Deserializer({ it?.let(this@optional::deserialize) })
inline fun <reified T> deserializer(): Deserializer<T> = if (null is T) lookup(T::class).optional() else lookup(T::class)
// call-site:
val d = deserializer<User>() // d is Deserializer<User>
val d1 = deserializer<User?>() // d1 is Deserializer<User?>, which is a wrapper over a Deserializer<User> (TIL
Not sure where this came from. The type of Sure, maybe some form of intersection types will let this compile, or an annotation or whatever. But because the function is going to be inlined anyway, why not just do this: inline fun <reified T : Any> deserializerFor(): Deserializer<T> = lookup(T::class)
inline fun <reified T : Any> deserializerForNullable(): Deserializer<T?> = lookup(T::class).optional() Or only have the first function and add |
Is the "& Any" becoming official? |
The |
It's borrowed from Java, which probably borrowed it from an older language but that was before my time so IDK. |
@quickstep24 I see now the point in this. @YoshiRulz Where do you see it in Java? In the code that caused it to appear (Glide in my case) I don't see in Java what has caused it... |
Bounds on type parameters can include intersection types, and it's certainly not a new feature. In Kotlin you'd use multiple |
@YoshiRulz So how does a non-null type appears in Java for the generic type ("T") ?
Where "Target" is as such:
I don't see R or T marked as "always not null". It's just that currently, all functions (I've shown only what's relevant to the function I use) have them non-null. |
I think there's been a slight misunderstanding; I never meant to imply that this feature as borrowed from Java, only that the |
@YoshiRulz Oh ok. |
¯\_(ツ)_/¯ |
@YoshiRulz OK guys thank you for your patience and for your time. |
Is this feature enforced since kotlin 1.9 and later? |
The goal of this proposal is to allow explicitly declare definitely not-nullable type
First version (obsolete): https://github.com/Kotlin/KEEP/blob/7b998efaf70cc8d783a57af14b8701886089a5fe/proposals/definitely-not-nullable-types.mdSecond version (11 Aug 2021): https://github.com/Kotlin/KEEP/blob/c72601cf35c1e95a541bb4b230edb474a6d1d1a8/proposals/definitely-non-nullable-types.md
Specific comments may be left here or at the #269
The text was updated successfully, but these errors were encountered: