Kotlin / Kotlin/dataframe

`Infer.Type` KDoc calls the given type an "upper bound", which it is not

Open
#2,090 1 comment 1 reaction 1 assignee View on GitHub

@Jolanrensen is already working on this.

Since Sep 17, 2026.

Dominant language
Kotlin
Stars
1.1k
Forks
83
Avg merge
4d 12h
Merged PRs (30d)
30

Description

Infer.Type KDoc calls the given type an "upper bound", which it is not

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/typeConversions.kt:259

Infer [DataColumn.type] and [DataColumn.hasNulls] from actual [DataColumn.values]
using an optionally provided base type as an upper bound.

The base type is not a bound. Whenever the column has values, the inferred type comes from
them alone and the given type leaves no trace — not even as a bound the result must satisfy.
It survives only for an empty column.

Reproducing test

Passes on master today; every line below contradicts the wording above.

@Test
fun `Infer Type does not treat the given type as an upper bound`() {
    val age = columnOf(15, 45, 20)

    // `String` is not a supertype of `Int`, yet the call succeeds and the bound leaves no trace
    age.map(typeOf<String>(), Infer.Type) { it }.type() shouldBe typeOf<Int>()

    // it does not bound a type argument either
    val lists = columnOf(listOf(1, 2), listOf(3))
    lists.map(typeOf<List<Number>>(), Infer.Type) { it }.type() shouldBe typeOf<List<Int>>()

    // the only case where the given type survives: there are no values to derive one from
    columnOf(*emptyArray<Int>()).map(typeOf<String>(), Infer.Type) { it }.type() shouldBe typeOf<String>()
}

typeOf<Number>(), typeOf<Boolean>() and typeOf<Any>() all give kotlin.Int here too.

Why

Iterable<KClass<*>>.createType (core/.../impl/TypeUtils.kt:182-196):

val upperClass = upperBound.classifier as KClass<*>
val baseClass = filter { it.isSubclassOf(upperClass) }.withMostSuperclasses() ?: withMostSuperclasses()
if (baseClass == null) {
    upperBound.withNullability(nullable)
} else {
    upperBound.projectTo(baseClass).withNullability(nullable)
}

When no actual class fits upperBound, the ?: falls back to the most-derived actual class and
projectTo discards the bound. baseClass == null — the one branch that keeps upperBound — is
reachable only when there are no values.

Scope

typeConversions.kt is shared by many operations, so the fix is documentation-only: describe the
parameter as a fallback for an empty column, not as an upper bound. The same wording was corrected
in api/map.kt and docs/StardustDocs/topics/map.md in #2066; this KDoc is where it was copied from.

Suggested wording

Infer [DataColumn.type] and [DataColumn.hasNulls] from actual [DataColumn.values].
An optionally provided base type is used only when there are no values to infer from;
it does not constrain the inferred type.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.