Scala 3 changed the signature of the auto-generated unapply method of case classes (see here). Unfortunately the unapply method is explicitly called when mapping forms for the play framework.
import play.api.data.Form
import play.api.data.Forms._
case class Thing(x: String, y: Int)
val f = Form(mapping(
"x" -> nonEmptyText(),
"y" -> number(),
)(Thing.apply)(Thing.unapply))
This snippet works in Scala2, but not in Scala3. The play documentation recommends to manually define a compatible unapply method, but i think we can do better.
And indeed, we can auto-generate the unapply method like this:
# Scala3 only
import scala.deriving.Mirror
object FormHelper:
def unapply[A <: Product](value: A)(using mirror: Mirror.ProductOf[A]): Option[mirror.MirroredElemTypes] =
Some(Tuple.fromProductTyped(value))
Defining forms now looks like this:
# Scala3 only
val f = Form(mapping(
"x" -> nonEmptyText(),
"y" -> number(),
)(Thing.apply)(FormHelper.unapply))
But now the problems begin. Given a case class with a single member:
case class Single(x: String)
My FormHelper.unapply returns an option of a tuple with a single element, like Option[Tuple1[T]]. But for some reasons the mapping function for play needs a flat Option[T].
So my question is: how can i extend my FormHelper (either with a new method, or extend the existing unapply) such that it returns Option[T] when given a case class with a single member.
The closest i could get to that was this
def single[A <: Product](value: A)(using mirror: Mirror.ProductOf[A]) = Some(Tuple.fromProductTyped(value).take(1))
The return type of that is Some[T *: EmptyTuple] and i'm not able to get rid of the EmptyTuple thing.
My guess is, that i somehow need to restrict the method to products of length 1, but unfortunately case classes only inherit from Product and not the specific ProductN[T...] traits.
Note: i know that i could use this to make the form definition work:
val f = Form(mapping(
"x" -> nonEmptyText(),
)(Single.apply)(t => Some(t._1))
but i'm also interested how to do that with the meta-programming approach.