-
Notifications
You must be signed in to change notification settings - Fork 449
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
Conversation
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()
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.
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 👍
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() |
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.
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
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 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?
Hey @nomisRev, wondering if there's anything needed here from my end before it's good to merge? |
Ah, I see that Set methods have changed from |
I think that is because of the 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 |
My understanding is that |
@mjadczak could you please run |
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.
🙌🏾
Sorry, I had missed your message - looks like you did this, thank you very much! |
The current implementation of
Iterable<A>.toNonEmptyListOrNull()
andIterable<A>.toNonEmptySetOrNull()
do not work correctly whenA
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 theIterable
overload (and of course a set is iterable).