Kotlin / kotlin-jupyter

Kotlin kernel for Jupyter/IPython
Apache License 2.0
1.1k stars 105 forks source link

Renderer resolution order #348

Open altavir opened 2 years ago

altavir commented 2 years ago

Consider the following situation: two jupyter integration have a renderer for the same type and are imported to the classpath. Currently, there are no wats to find, which one will be used. Obviously, the situation is not far-fetched. There could be different renderers for commonly used classes like a Map.

In VisionForge I am solving a similar problem by assigning a rating number and then selecting a renderer with the highest rating:

https://github.com/mipt-npm/visionforge/blob/3caa22f8bf0bad62ea9d2b123e4f60a1b89eeaf1/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/elementOutput.kt#L15

Libraries could declare renderers with higher than default rating allowing developers to declare that one renderer is better than the default one. There could be other solutions as well.

ileasile commented 2 years ago

Maybe more generic approach

interface ComparableRenderer : Comparable<Renderer>, Renderer

fun compare(r1: Renderer, r2: Renderer): Int {
    return if (r1 is ComparableRenderer) {
         r1.compareTo(r2)
    } else {
         if (r2 is ComparableRenderer) -r2.compareTo(r1)
         else 0
    }
}

abstract class PriorityComparableRenderer(val priority: Int = 10) : ComparableRenderer {
    override fun compareTo(other: Renderer): Int {
       return if (other is PriorityComparableRenderer) return priority.compareTo(other.priority)
       else 0
    }
}
ileasile commented 2 years ago

"Electors" approach

fun interface RenderersElector {
    fun elect(renderers: Collection<Renderer>): Collection<Renderer>
}

fun electMax(renderers: Collection<Renderer>): Renderer {
    return electors.fold(renderers) { acc, elector -> elector.elect(acc) }.first() // in ideal case, should remain single
}
altavir commented 2 years ago

Both these approaches compare renderers without any knowledge about the object being rendered. If we are talking about generic, it probably would be better to provide an actual object for the selector. Also, it is not clear, who will select a selector. If the user just loads two integrations (libraries? plugins? do we have an official name?), he won't have any prompt on choosing which to use. An overriding strategy would be nice, but by default, he would expect that they will decide themselves.