-
Notifications
You must be signed in to change notification settings - Fork 21
Java interop version of toOptional() #17
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
Conversation
artem-zinnatullin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm putting "Request changes" for now just to block merge until we have consensus on 2.x release, no actual changes in PR required :)
| @@ -1,3 +1,5 @@ | |||
| @file:JvmName("Optional") | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I 100% agree with this change, the only problem is that it's a breaking change for Java consumers (class name change) and we'll need to make a 2.x release for that
However I'm not opposed to that
@dmitry-novikov @ming13 @nostra13 are you ok with 2.x release?
If yes — we'll need separate issue and PRs for that (Group name will also need to be updated to com.gojuno.koptional2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with bumping major version to indicate breaking change, but against package change because is will affect a lot of consumers and most of them are Kotlin based. So I would better force Java consumers to refactor their codebase because of class name change =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@artem-zinnatullin, you are too much in all this package change for no reason thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s make a minor release and that’s it. Koptional is a Kotlin-first library, I see no harm in changing Java compatibility as a minor change. It is OK to change since it is no radical. Otherwise every library should follow Chrome or Firefox versions scheme and have versions in double or triple digits since API change all the time and hey, there are artifact versions exactly for that. Artem just has some kind of fashion taste in bumping package names (even for internal libraries with a single user, yep) :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that Koptional is Kotlin-first, I'd agree that this is a minor change, even though it breaks Java consumers. Changing the package name will require Kotlin consumers to change imports everywhere, and bumping the major version feels excessive for such a small change - compare it with RxJava 2 vs RxJava 1 or Dagger 2 vs Dagger 1. Even though it's breaking, it's trivial to fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's trivial to fix for sure (basically find + replace for package name)
However because Koptional was initially designed to be very minimalistic it might be used by other libraries which means that in such a case we're breaking compatibility between user's code and other library.
That's why i suggest both major version bump and groupid update :)
GitHub search results: https://github.com/search?utf8=%E2%9C%93&q=com.gojuno.koptional&type=Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Search shows no usage from Java code, so...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which doesn't mean there are no private Java libs that depend on class name…
This change is still breaking even for @Egorand himself because they use Koptional from Java (that's the point of PR), which means that we should at least bump major version https://semver.org/
Do we need to add number to groupId and package name?
I think yes, it's very trivial to migrate to new one in same commit as dependency update and we allow users to migrate their own code and dependencies gradually in case of class-name problem
I might not agree with everything Jake thinks but http://jakewharton.com/java-interoperability-policy-for-major-version-updates/ is very good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s assume we have more Kotlin users than Java users. Changing a package name will force all users to do a Find + Replace. Changing a Java name will do the same for Java users only. I’m in for the version change, but against the package change. I totally understand all your points and, believe me, I would be on your side regarding anything related to these kind of changes. But! This PR does not introduce any behavior changes. If .toNullable was changed, that’s for sure forces the package change. At the same time, the library is so tiny I cannot even believe we are arguing about this 😆
|
I'm just a consumer of the API (private repo, would be affected by the breaking change but not in a way I couldn't recover from in 5 minutes), but it seems that the argument can be distilled into two things:
Let me ask a rhetorical question -- when driving, do you use a turn signal when you think nobody is looking? IMO, it's best to do the "right thing" even when nobody's looking. |
|
@tbsandee, I just got some serious 13 Reasons Why vibe. Still not convinced that renaming can be considered a behavior change though. I’m kind of in, but still consider this case an extreme over pragmatism at its best. The worst thing I can imagine right now is related to an argument from Artem about
Should we provide an interop then? I need a drink. |
|
I sure hope it's a good drink :D |
|
We don't have to provide interop (although we can):
|
|
Soooo? |
|
Can I go ahead and:
? Pros:
Cons:
I'd like to get this going. |
|
@artem-zinnatullin, I’m in. Still sounds ridiculous for such a small library, but we made a mistake not taking Java into account and we are gonna pay the price of having |
|
Kool, implementing then 👍 |
|
@Egorand there is a compilation error (you can see it on CI): e: /opt/project/koptional/src/main/kotlin/com/gojuno/koptional/Optional.kt: (1, 1): Duplicate JVM class name 'com/gojuno/koptional/Optional' generated from: package-fragment com.gojuno.koptional, Optional
e: /opt/project/koptional/src/main/kotlin/com/gojuno/koptional/Optional.kt: (5, 1): Duplicate JVM class name 'com/gojuno/koptional/Optional' generated from: package-fragment com.gojuno.koptional, Optional |
vanniktech
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there also be a Java test to verify this change?
|
That'd be nice! |
|
@Egorand can you please update PR to make it compile and add few Java test cases? |
|
Sure, will update the PR tomorrow |
f47cb99 to
3404519
Compare
|
I think I've actually discovered a better solution that won't break existing clients and will improve Java experience: adding a companion object with an extra
|
So, What if we "hide" the first If you add |
|
That would be a breaking change, since Java clients who were using |
|
Yeah, we already agreed on 2.x release though. Would be great to make initial approach with single Technically it seems okay to have Another solution is to write Koptional core in Java with proper Kotlin annotations and properly placed functions and see if Kotlin compiler works with that, I mean, that sounds like a fun experiment (not that I have time for it tho…) |
|
The problem is that extension functions declared inside classes or objects can only be used as extensions inside those classes/objects, hence you'll need to always do |
|
Let’s go ahead without hacks and do a |
|
So, actually, what do we agree on? |
|
As I mentioned, the change is not breaking, so it'd be fine as a minor update |
|
@ming13, @artem-zinnatullin, are you OK to merge it as minor update? |
|
ok, I double checked current version of PR, I think I'm good to merge this as non breaking change 👍 Kotlin:
Java:
@Egorand just couple things:
|
|
Yeah no worries! I'll try to get the changes in today |
artem-zinnatullin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, few comments
| companion object { | ||
|
|
||
| /** | ||
| * Wrap an instance of T (or null) into an [Optional]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
supernit: Wrap[s]
| /** | ||
| * Wrap an instance of T (or null) into an [Optional]: | ||
| * | ||
| * ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we specify language here?
Like ```java
|
|
||
| fun <T : Any> T?.toOptional(): Optional<T> = if (this == null) None else Some(this) | ||
| /** | ||
| * Wrap an instance of T (or null) into an [Optional]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
| * using the static [Optional.toOptional] method. | ||
| */ | ||
| @Suppress("NOTHING_TO_INLINE") | ||
| inline fun <T : Any> T?.toOptional(): Optional<T> = if (this == null) None else Some(this) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry for not being clear, that's not what I meant :)
Inlining this extension everywhere can inflate user's class files noticeably, we intentionally didn't make this one inline
What I was talking about is that we can copy-paste this body to the @JvmStatic one so we don't have to do two method calls just because we're calling it from Java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got your point initially, just thought inlining would be a better option here, as opposed to duplication. Since it's a one-liner I don't see it noticeably affecting the bytecode size, but I can change it to just use the same code as the other version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, well it's one-liner in Kotlin 😸 , bytecode is not that small
L0
LINENUMBER 21 L0
ALOAD 0
IFNONNULL L1
GETSTATIC com/gojuno/koptional/None.INSTANCE : Lcom/gojuno/koptional/None;
CHECKCAST com/gojuno/koptional/Optional
GOTO L2
L1
NEW com/gojuno/koptional/Some
DUP
ALOAD 0
INVOKESPECIAL com/gojuno/koptional/Some.<init> (Ljava/lang/Object;)V
CHECKCAST com/gojuno/koptional/Optional
L2
ARETURN
L3
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L3 0
MAXSTACK = 3
MAXLOCALS = 1
Wonder why it does CHECKCAST com/gojuno/koptional/Optional, seems useless, maybe we need to update compiler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah well, hidden costs of Kotlin!
| */ | ||
| @JvmStatic | ||
| fun <T : Any> toOptional(t: T?): Optional<T> = t.toOptional() | ||
| fun <T : Any> toOptional(t: T?): Optional<T> = if (t == null) None else Some(t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh, parameter name t is part of public API in Kotlin, if you can come up with something more meaningful but short that'd be nice
I have obj on mind, but idk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! What about value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better, let's use that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
artem-zinnatullin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noice! Let's wait for review from the others now :)
|
Sorry for delay, had miscommunication with Artur and was waiting for his review while he was waiting for me to merge heh |
|
Sweet! Are you planning a release any time soon? |
|
Yep, we can do a release with this PR only or wait for rest |
|
@Egorand 1.4.0 was just released with this change, guys at Juno have some workload backpressure, I'm gonna take care of processes for some time |
We're sometimes calling
toOptional()from Java and it would be more convenient to haveOptional.toOptional()instead ofOptionalKt.toOptional().