Skip to content

Add persistent Collection builder functions #166

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Goooler
Copy link
Contributor

@Goooler Goooler commented Dec 29, 2023

Closes #137.

@Goooler

This comment was marked as outdated.

Copy link
Contributor

@qurbonzoda qurbonzoda left a comment

Choose a reason for hiding this comment

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

Documentation for the functions should be amended. Otherwise, LGTM.

@Goooler Goooler force-pushed the add-build-extensions branch from bc52615 to 20ad7a1 Compare January 4, 2024 18:28
@qurbonzoda qurbonzoda requested a review from ilya-g January 5, 2024 09:14
Comment on lines +774 to +775
* The list passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
Copy link
Contributor

Choose a reason for hiding this comment

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

Builders in kotlinx.collections.immutable are capable of constructing a new persistent collection multiple times. However, I'm struggling to identify a compelling use case for retaining a reference to the builder and utilizing it outside the builderAction. If we constraint the builder to be valid only within the function, could using a MutableList as the receiver be a more performance-efficient choice?

Copy link
Contributor

Choose a reason for hiding this comment

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

cc @ilya-g, WDYT?

Copy link

Choose a reason for hiding this comment

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

It feels like if that were the case, mutate would already be optimizing this in the same way, no?

@Goooler Goooler force-pushed the add-build-extensions branch 2 times, most recently from 62dfb51 to c90d590 Compare January 18, 2024 10:30
@Goooler Goooler force-pushed the add-build-extensions branch from c90d590 to 1ac235e Compare February 27, 2024 03:27
Comment on lines 781 to 779
@OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class)
public inline fun <T> buildPersistentList(@BuilderInference builderAction: PersistentList.Builder<T>.() -> Unit): PersistentList<T> {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return persistentListOf<T>().builder().apply(builderAction).build()
}
Copy link
Contributor Author

@Goooler Goooler Feb 27, 2024

Choose a reason for hiding this comment

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

  1. Annotate @BuilderInference for better generic type support.
  2. Add InvocationKind.EXACTLY_ONCE contract to indicate the function parameter will be invoked exactly one time.

Ref https://github.com/JetBrains/kotlin/blob/3ff2bc403cb64cee273833d4dbbaf6d8c928edbc/libraries/stdlib/src/kotlin/collections/Collections.kt#L180-L187

Copy link

Choose a reason for hiding this comment

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

AFAIK @BuilderInference is a no-op, and has been for a while now. My understanding is that builder inference is applied by default everywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't remove it to keep the same behaviors as the builders in stdlib.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed now.

Copy link

Choose a reason for hiding this comment

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

The opt-in for ExperimentalTypeInference::class is unnecessary now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

@Goooler Goooler force-pushed the add-build-extensions branch from 1ac235e to f4b5f15 Compare September 4, 2024 20:07
@OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class)
public inline fun <T> buildPersistentList(@BuilderInference builderAction: PersistentList.Builder<T>.() -> Unit): PersistentList<T> {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return persistentListOf<T>().builder().apply(builderAction).build()
Copy link

Choose a reason for hiding this comment

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

Could use mutate instead. Same for the other builder functions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Replaced.

@Goooler Goooler force-pushed the add-build-extensions branch 2 times, most recently from c2ade34 to 505c5c2 Compare May 25, 2025 01:59
@kyay10
Copy link

kyay10 commented May 25, 2025

It looks like you lost the InvocationKind.EXACTLY_ONCE contract while refactoring. Could you add it back in please?

@Goooler
Copy link
Contributor Author

Goooler commented May 25, 2025

image

Leaked in-place lambda 'builderAction: MutableList.() -> Unit'.
Wrong invocation kind 'EXACTLY_ONCE' for 'builderAction: MutableList.() -> Unit' specified, the actual invocation kind is 'AT_MOST_ONCE'.

@kyay10
Copy link

kyay10 commented May 25, 2025

Oooo, looks like mutate might have an incorrect contract then! I think it'd be reasonable to add contracts to mutate while you're at it, since it's in the same file, and does very similar things to the builders

@Goooler Goooler force-pushed the add-build-extensions branch from 505c5c2 to d0eac5a Compare May 25, 2025 02:13
@Goooler
Copy link
Contributor Author

Goooler commented May 25, 2025

public inline fun <T> PersistentList<T>.mutate(mutator: (MutableList<T>) -> Unit): PersistentList<T> = builder().apply(mutator).build()

builder and apply are applied for mutate, and I see the contract is already on the apply:

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

@kyay10
Copy link

kyay10 commented May 25, 2025

Yes, but contracts currently aren't inferred nor inherited automatically like this, so sadly you have to explicitly add the contract to mutate. If you will, contracts are part of the API surface.

@Goooler
Copy link
Contributor Author

Goooler commented May 25, 2025

I believe we should add contracts in a separate PR.

@Goooler Goooler force-pushed the add-build-extensions branch from d0eac5a to 16df77e Compare May 25, 2025 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants