To create all possible combinations of two sets of parameters and perform an action on them, you can do:
setOf(foo, bar, baz).forEach { a ->
setOf(0, 1).forEach { b ->
/* use a and b */
}
}
However, if you have (potentially many) more parameters, this quickly turns into a pyramid of doom:
setOf(foo, bar, baz).forEach { a ->
setOf(0, 1).forEach { b ->
setOf(true, false, null).forEach { c ->
setOf("Hello,", "World!").forEach { d ->
/* use a, b, c and d */
}
}
}
}
You could write this similarly with for
loops, or differently like so:
val dAction = { d: String -> /* use a, b, c and d */ }
val cAction = { c: Boolean? -> setOf("Hello,", "World!").forEach(dAction) }
val bAction = { b: Int -> setOf(true, false, null).forEach(cAction) }
val aAction = { a: Any? -> setOf(0, 1).forEach(bAction) }
setOf(foo, bar, baz).forEach(aAction)
But I don't think that's any better, because there are some readability issues here: d, c, b and a's actions are written in reverse. Their type specifications cannot be inferred, so they must be specified. It's reversed sequentially compared to the pyramid of doom. The order of the sets providing the possible values should not matter, but it does: you just want to create any combinations from a bunch of sets, however, in this code every line depends on the previous.
It would be very nice to have an idiomatic way of doing something like Python's or Haskell's comprehensions, in which you (almost like the mathematical notation) can do something like:
{ /* use a, b, c and d */
for a in setOf(foo, bar, baz),
for b in setOf(0, 1),
for c in setOf(true, false, null),
for d in setOf("Hello,", "World!")
}
Which is very easy to read: there is no excessive indentation, the action you're interested in goes first, the data sources are very clearly defined, etc.
Side note: similar problems occur with flatMap
-flatMap
-...-flatMap
-map
.
Any ideas about how to neatly create n-ary cartesian products in Kotlin?
Pair<T, U>
tuple type, with type information. The same could be made forTriple<T, U, V>
tuples. See my answer below for a more general solution for any size. See other answers for other approaches, e.g. using Arrow-KT. That lib also provides typed tuple types for many numbers of parameters, e.g. see here: arrow-kt.io/docs/meta/apidocs/prelude/arrow.tuples/index.html. – Unreserve