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

Fix .toNonEmptyXxxOrNull for nullable types #3127

Merged
merged 11 commits into from
Oct 26, 2023
Merged

Conversation

mjadczak
Copy link
Contributor

@mjadczak mjadczak commented Sep 7, 2023

The current implementation of Iterable<A>.toNonEmptyListOrNull() and Iterable<A>.toNonEmptySetOrNull() do not work correctly when A is a nullable type (if the first element of the iterable is null, the function returns null). The implementation also gets the iterator of the iterable more than once. See #3123 .

This PR changes the implementation of both to correctly handle the nullable case, as well as ensuring the iterable is only iterated once. The overload of the latter function with Set<A> as the receiver is also removed, as there is no difference in behaviour or functionality between it and the Iterable overload (and of course a set is iterable).

Matt Jadczak added 2 commits September 7, 2023 07:01
For case when .toNonEmptyListOrNull and .toNonEmptySetOrNull are called
on an iterable starting with null
This also ensures that we iterate the iterable only once.

This also removes the Set<A> receiver versions of .toNonEmptySetOrNull()
and .toNonEmptySetOrNone()
@mjadczak mjadczak changed the title Fix .toNonEmptyXxxOrNull for nullable types (#3123) Fix .toNonEmptyXxxOrNull for nullable types Sep 7, 2023
Copy link
Member

@nomisRev nomisRev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR, and your first contribution @mjadczak! 🙌

Just one small issue with the binary breaking change, I proposed two potential solutions. Let me know what you think 👍

Comment on lines 63 to 67
public fun <A> Set<A>.toNonEmptySetOrNull(): NonEmptySet<A>? =
firstOrNull()?.let { NonEmptySet(it, minus(it)) }

public fun <A> Set<A>.toNonEmptySetOrNone(): Option<NonEmptySet<A>> =
toNonEmptySetOrNull().toOption()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These either need to be annotated with @Deprecated("Same as Iterable operator", DeprecationLevel.HIDDEN) or we can keep them and perhaps get a slightly faster implementation. Otherwise it's going to affect the binary, which is currently failing the PR.

if(it.isNotEmpty()) first().let { NonEmptySet(it, minus(it)) } else null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I missed that this would cause a break.

Unfortunately this would not be faster - below is the implementation of minus for Set (I too miss Scala's structural sharing)

public operator fun <T> Set<T>.minus(element: T): Set<T> {
    val result = LinkedHashSet<T>(mapCapacity(size))
    var removed = false
    return this.filterTo(result) { if (!removed && it == element) { removed = true; false } else true }
}

however, given that NonEmptySet does not store its first element separately, unlike NonEmptyList, we could have a fast path for this by adding an internal fun fromSetUnsafe to the companion object and using that here in the case that the set is not empty?

@mjadczak
Copy link
Contributor Author

Hey @nomisRev, wondering if there's anything needed here from my end before it's good to merge?

@mjadczak
Copy link
Contributor Author

Ah, I see that Set methods have changed from fun to synthetic fun in the binary API - I'm guessing that's because they now just redirect to another method? I've had a look around but could find no information on this - do you know if there is an attribute or similar to force the compiler to keep the explicit method?

@nomisRev nomisRev requested review from serras and a team and removed request for a team September 25, 2023 18:08
@nomisRev
Copy link
Member

Ah, I see that Set methods have changed from fun to synthetic fun in the binary API

I think that is because of the HIDDEN, which shouldn't be a problem 🤔 @serras any idea? I cannot find anything in the documentation https://github.com/Kotlin/binary-compatibility-validator.

To merge we should get a second review, if we didn't get one this week I think we can merge. I am going to ask around for the synthetic on Slack.

@serras
Copy link
Member

serras commented Sep 29, 2023

My understanding is that synthetic does not affect ABI compatibility, the JVM doesn’t see them differently. The difference is that Java (the language) doesn’t “see” the elements marked as synthetic, but that shouldn’t be a problem here either.

@serras
Copy link
Member

serras commented Oct 19, 2023

@mjadczak could you please run ./gradlew apiDump on this (updated) branch and commit the results? The automic API updater doesn't work on forks.

Copy link
Member

@i-walker i-walker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌🏾

@mjadczak
Copy link
Contributor Author

@mjadczak could you please run ./gradlew apiDump on this (updated) branch and commit the results? The automic API updater doesn't work on forks.

Sorry, I had missed your message - looks like you did this, thank you very much!

@serras serras merged commit 1a2927b into arrow-kt:main Oct 26, 2023
10 checks passed
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

Successfully merging this pull request may close these issues.

None yet

4 participants