Converting between tuples and case classes in Scala 3

Niklas Klein
2 min readNov 6, 2021

--

“Idea” by @jdiegoph

Converting a case class into its tuple representation and vice versa is a frequently discussed topic by Scala developers on Stackoverflow. This is not least due to the fact that Scala did not offer an adequate solution for this before version 3.

The most versatile solution to this problem was offered by the generic programming library Shapeless.

import shapeless._
import shapeless.ops.hlist.Tupler
object Tuples {
def to[A, B <: HList, C](value: A)(
implicit
generic: Generic.Aux[A, B],
tupler: Tupler.Aux[B, C]
): C = Generic[A].to(value).tupled
def from[A]: FromBuilder[A] = new FromBuilder[A] final class FromBuilder[A] {
def apply[B <: HList, C](value: C)(
implicit
untupler: Generic.Aux[C, B],
generic: Generic.Aux[A, B]
): A = generic.from(untupler.to(value))
}
}
final case class Vehicle(manufacturer: String, wheels: Int)Tuples.to(Vehicle(manufacturer = "Lada", wheels = 4))
// ("Lada", 4)
Tuples.from[Vehicle](("Simson", 2))
// Vehicle("Simson", 2)

Luckily, with Scala 3, the language now has strong generic programming capabilities built around tuples. So the Shapeless based implementation from above can now be rewritten without external dependencies.

import scala.deriving.*

object Tuples:
def to[A <: Product](value: A)(
using mirror: Mirror.ProductOf[A]
): mirror.MirroredElemTypes = Tuple.fromProductTyped(value)

def from[A](value: Product)(
using
mirror: Mirror.ProductOf[A],
ev: value.type <:< mirror.MirroredElemTypes
): A = mirror.fromProduct(value)

final case class Vehicle(manufacturer: String, wheels: Int)

Tuples.to(Vehicle(manufacturer = "Lada", wheels = 4))
// ("Lada", 4)
Tuples.from[Vehicle](("Simson", 2))
// Vehicle("Simson", 2)

The Scala 3 based solution not only works without additional dependencies, but is also faster to compile and will eventually work better with your code editor of choice than the Shapeless based solution.

--

--