IDK, maybe I have a brain freez, but I can't figur...
# announcements
t
IDK, maybe I have a brain freez, but I can't figure out how to model it without lower bounds. (The apply method, that takes list and returns new list with modifications applied)
Copy code
interface ListModification<out E> {
	fun applyTo(target: MutableList<in E>)
	fun <T> apply(target: List<T>) : List<T>
			where E : T
}
n
this is tricky because you're using E in both an in and out position, so I'd imagine has to be invariant.
Copy code
interface ListModification<E> {
    fun applyTo(target: MutableList<E>)
    fun apply(target: List<E>) : List<E>
}
does this not work for you?
👆 1
dunno you might need to split the interface
t
I don't want to make it invariant, as it does not need to be invariant(semantically).
n
Copy code
interface ListModification<E> {
    fun applyTo(target: MutableList<E>)
    fun <T : E> apply(target: List<E>) : List<T>
}
?
even if
E
==
T
, should make the types shake out
n
you'd have to manually specify
T
for apply
which probably isn't the API you'd want
t
@MaxAller The ListModification interface may add element to list, so this is invalid, as you would allow T to be String when E is Int.
n
I guess I don't follow how this is not invariant, semantically
👆 1
t
Ah, sorry. I noticed you removed variance from Change interface...
n
I guess I'm confused how the apply method's type parameter is related to E
t
But I want to be able to do things like:
Copy code
object Clear : ListManipulation<Nothing>
@Nir It's not invariant, as I do not allow transforming elements in this type hierarchy. So you can add elements, remove them, permutate, copy or any combination of those.
n
you may have to use unsafe variance, though I'm not yet certain or not whether this is a valid usage
kotlin's immutable data structures collection uses the unsafe variance annotation quite often
t
So, naturally:
Copy code
fun <T> List<T>.apply(change: ListModification<T>)
(T is contravariant in this case)
*
Copy code
fun <T> List<T>.apply(change: ListModification<T>) : List<T>
n
yeah, it seems like a similar situation to immutable
t
@Nir I would be fine with using unsafe variance if proven necessary internally in implementation. But not in public API.
n
if i understand you correctly; pretty sure this is mandatory if you want to achieve the variance and signatures you want
Here's a snippet from Kotlin's immutable collections
Copy code
public interface PersistentList<out E> : ImmutableList<E>, PersistentCollection<E> {
    /**
     * Returns a new persistent list with the specified [element] appended.
     */
    override fun add(element: @UnsafeVariance E): PersistentList<E>
seems very similar to your issue
n
can confirm, this doesn't quite compile :)
n
basically, you say that you're
out E
but you still have functions, that, by their signatures, could read elements passed by the user, so you need to basically reassure the type system that you pinky swear not to actually read those elements
t
Not sure what you mean exactly by "read". Check this example:
Copy code
sealed class MetaChange : ListModification<Nothing> {

        override fun applyTo(target: MutableList<in Nothing>) {
            applyMetaChange(target)
        }

        abstract fun <E> applyMetaChange(target: MutableList<E>)

    }
n
that said, I'm not sure it's as safe in your case (semantically) as it is in this example
t
Copy code
data class Swap(
        val aIndex: Int,
        val bIndex: Int
    ) : PermutationChange(), StandardChange {
        override fun <T> applyMetaChange(target: MutableList<T>) {
            val element = target.removeAt(aIndex)
            target.add(bIndex, element)
        }
    }
Whatever I can do to mutable list, I should be able to do to a copy of the list.
I feel now like the variaince is not complete without lower bounds.
Ahh, can't Java it as well. But Scala does support lower bounds and it works fine.
https://youtrack.jetbrains.com/issue/KT-209 this is this case. (Analogical to one @Nir posted)
n
yeah, seems like the analogy is accurate
so a lower bound would give you a way to express this within the type system without throwing unsafe variance at it
There are some other good use cases for lower bounds in the link
Thanks for posting that, I was thinking a bit on whether lower bounds had enough utility before drifting back to work, this is pretty compelling
t
Anyway, thanks for ideas : )
n
thank you, I learned some stuff 🙂