Skip to main content

Pair and Triple in public API

PAIR_OR_TRIPLE_PUBLIC_API reports the tuple types Pair and Triple in publicly visible signatures.

DiagnosticPAIR_OR_TRIPLE_PUBLIC_API
Default severityError
Gradle propertypairOrTriplePublicApi
Exemption@IntentionallyPairOrTriple

What it reports

The check reports Pair and Triple in return types, property types, parameter types, and type parameter bounds, including their type arguments (like List<Pair<Int, String>>):

@file:JvmName("Locations")
/** Returns an unnamed location. */
public fun locate(): Pair<Int, Int> = 0 to 0

Rationale

Pair and Triple name their components first/second/third, so a call site reading point.first or destructuring val (a, b) = point learns nothing about what the values mean. Worse, the shape is fixed: it can't grow a fourth component or rename a component without breaking every user in a source-incompatible way, while a purpose-built class can add an optional property with a default value. See the Kotlin API guidelines on object-oriented design for data and state.

Don't

@file:JvmName("Geometry")
/** Returns unnamed dimensions. */
public fun dimensions(): Triple<Int, Int, Int> = Triple(0, 0, 0)
/**
* Attaches content to an unnamed coordinate pair.
*
* @property position horizontal and vertical anchor coordinates.
*/
@Poko
public class Anchor(public val position: Pair<Int, Int>)
/** Returns unnamed edges. */
public fun edges(): List<Pair<Int, Int>> = emptyList()

Do

@file:JvmName("Geometry")
/**
* Extents of a three-dimensional object.
*
* @property width horizontal extent in pixels.
* @property height vertical extent in pixels.
* @property depth front-to-back extent in pixels.
*/
@Poko
public class Dimensions(
public val width: Int,
public val height: Int,
public val depth: Int,
)
/** Returns the dimensions. */
public fun dimensions(): Dimensions = Dimensions(0, 0, 0)
/**
* A position in Cartesian coordinate space.
*
* @property x distance from the vertical axis.
* @property y distance from the horizontal axis.
*/
@Poko
public class Point(
public val x: Int,
public val y: Int,
)
/**
* Attaches content to a point in Cartesian space.
*
* @property position point at which the content is anchored.
*/
@Poko
public class Anchor(public val position: Point)
/** Returns edges. */
public fun edges(): List<Point> = emptyList()

Notes

  • @PublishedApi internal declarations are not reported because their tuple types do not cross the supported source API boundary.
  • A tuple type parameter bound (<T : Pair<Int, Int>>) is reported too: it constrains every instantiation to the tuple shape, exposing it just like a direct mention.
  • Extension receivers are not reported: fun Pair<Int, Int>.manhattanLength(): Int serves a value the user already holds instead of handing out a new tuple.
  • Overrides are not reported: their signature is fixed by the overridden declaration, which is reported instead.

Exemption

Apply @IntentionallyPairOrTriple on the whole declaration, on a single parameter or type parameter, or on a type usage, where it covers the annotated type and everything nested in it:

@file:JvmName("Geometry")
/** Returns deliberately unnamed dimensions. */
@IntentionallyPairOrTriple(reason = ExemptionReason.API_DESIGN)
public fun dimensions(): Triple<Int, Int, Int> = Triple(0, 0, 0)
/**
* Attaches content to a deliberately unnamed coordinate pair.
*
* @property position horizontal and vertical anchor coordinates.
*/
@Poko
public class Anchor(
public val position: @IntentionallyPairOrTriple(
reason = ExemptionReason.API_DESIGN,
) Pair<Int, Int>,
)
/** Returns deliberately unnamed edges. */
@IntentionallyPairOrTriple(reason = ExemptionReason.API_DESIGN)
public fun edges(): List<Pair<Int, Int>> = emptyList()

Configuration

apiWatchdog {
pairOrTriplePublicApi = WatchdogSeverity.WARNING
}

With direct compiler invocation:

-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=PAIR_OR_TRIPLE_PUBLIC_API:warning

See also