I'm pretty sure I already know its not possible in...
# getting-started
d
I'm pretty sure I already know its not possible in Kotlin, but I'll double check here. I want to have a return type dependent on an interface type parameter and function type parameter.
Copy code
interface ExtensibleAwareKey<E</* magic syntax here*/>> {
    fun <B> of(type: KClass<B>): ExtensionKey<E<B>>
}

inline fun <reified B, E</*magicsyntax*/>> ExtensibleAwareKey.of() = of(B::class)

data class TypeDependentKey<B, E>(val type: KClass<B>) : ExtensionKey<E>

// example implementation:
class Describable<E> {

    companion object : ExtensibleAwareKey<Describable</*magic*/>> {
        override fun <B> of(type: KClass<B>): ExtensionKey<Describable<B>> = TypeDependentKey<B, Describable<B>>(type)
    }
}
r
I'm not sure what you're trying to do, but I think you have your return type wrong in the companion, You're returning
TypeDependencKey<B, ...>
which is
ExtensionKey<B>
, but your function expects
ExtensionKey<Describable<B>>
.
d
After some ChatGPT conversation, I guess what I'm asking about is called "higher-kinded types". It doesn't look like kotlin supports it. Also, I typoed
TypeDependentKey
, it should extend
ExtensionKey<E>
. I've edited my original question.
r
Ah, I see. I'm not particularly familiar with a lot of the details of functional programming (which is believe is where higher kinds comes from), but I think #C5UPMM0A0 adds a lot of functional stuff to the language. Perhaps you could ask there.
d
I'm not entirely sure its Functional programming. I'm more familiar with it in TMP with C++.
r
I also tried rewriting your code just to get it to work (though it may not be what you want). This at least compiles and runs, if you want to see if it helps: https://pl.kotl.in/wTMB9Qbwg
Fair enough. Like I said, definitely not my field of expertise, but there are some pretty good engineers on the Arrow project, so it may be worth asking them anyway.
d
I reworked it myself. It needs a lot more scaffolding to work correctly, but I did get it to function how I wanted.
👍 1
Extensible.kt
Copy code
interface Extensible {
    fun <E : Any> getExtension(key: ExtensionKey<E>): E?
    fun <E : Any> putExtension(key: ExtensionKey<E>, value: E)
    fun <E : Any> getOrPut(key: ExtensionKey<E>, initializer: () -> E): E
    operator fun contains(key: ExtensionKey<*>): Boolean
    fun remove(key: ExtensionKey<*>)
    fun removeIf(predicate: EntryPredicate)
    fun keepIf(predicate: EntryPredicate) = removeIf(predicate.not())
    fun forEach(visitor: EntryVisitor)
}

operator fun <B : Extensible, E : Any> B.get(key: TypeDependentExtensionKey<B, E>): E? =
    getExtension(key)

operator fun <E : Any> Extensible.get(key: ExtensionKey<E>): E? = getExtension(key)

operator fun <B : Extensible, E : Any> B.set(key: TypeDependentExtensionKey<B, E>, value: E) =
    putExtension(key, value)

operator fun <E : Any> Extensible.set(key: ExtensionKey<E>, value: E) = putExtension(key, value)
TypeDependentExtensionKey.kt
Copy code
open class TypeDependentExtensionKey<B : Extensible, E : Any>(val type: KClass<B>, val equality: Any) :
    ExtensionKey<E> {

    companion object {
        inline operator fun <reified B : Extensible, E : Any> invoke(equality: Any) =
            TypeDependentExtensionKey<B, E>(B::class, equality)
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is TypeDependentExtensionKey<*, *>) return false
        return type == other.type && equality == other.equality
    }

    override fun hashCode(): Int {
        var result = type.hashCode()
        result = 31 * result + equality.hashCode()
        return result
    }
}
Describers.kt
Copy code
class Describers<B : Any> internal constructor(
    private val builder: DescribersBuilderImpl<B> = DescribersBuilderImpl(),
) : DescribersBuilder<B> by builder {
    
    companion object {
        inline operator fun <reified B : Extensible> invoke() = TypeDependentExtensionKey<B, Describers<B>>(this)
    }
}
The downside is I have to make a function call instead of simply reference an object:
myExtension[Describers()]
as opposed to
myExtension[Describers]
r
Does that work as expected? The
this
in your
Describers
companion is pointing at the companion, not at an instance of
Describers
, so I don't see what good it does for the sake of an equality comparison (though I may just be missing the point of the code)
d
That's just to distinguish
Describer keys
from
Other extension keys
.
👍 1
So, two "TypeDependentExtensionKey" are only equal if they are the same type of extension, and the same dependent type.
Describer.Companion is the marker for the "kind" of extension.