Java interop version of toOptional()#17
Conversation
artem-zinnatullin
left a comment
There was a problem hiding this comment.
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.
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.
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.
@artem-zinnatullin, you are too much in all this package change for no reason thing.
There was a problem hiding this comment.
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.
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.
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.
Search shows no usage from Java code, so...
There was a problem hiding this comment.
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.
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.
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.
Looks good, few comments
| companion object { | ||
|
|
||
| /** | ||
| * Wrap an instance of T (or null) into an [Optional]: |
There was a problem hiding this comment.
supernit: Wrap[s]
| /** | ||
| * Wrap an instance of T (or null) into an [Optional]: | ||
| * | ||
| * ``` |
There was a problem hiding this comment.
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]: |
| * 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.
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.
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.
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.
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.
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.
Good catch! What about value?
There was a problem hiding this comment.
Much better, let's use that!
artem-zinnatullin
left a comment
There was a problem hiding this comment.
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().