I'm trying to map an ADT (case classes inheriting from a sealed trait) as multiple columns of the same table, using Slick, something like:
sealed trait OrValue
case class IntValue(value: Int)
case class StringValue(value: String)
case class Thing(id: Int, value: OrValue)
class Things(tag: Tag) extends Table[Thing](tag, "things") {
def id = column[Int]("id", O.PrimaryKey)
def intValue = column[Option[Int]]("intValue")
def stringValue = column[Option[String]]("stringValue")
def toThing(_id: String, _intValue: Option[Int], _stringValue: Option[String]): Thing = Thing(id, ((_intValue, _stringValue) match {
case (Some(a), None) => IntValue(a)
case (None, Some(a)) => StringValue(a)
}
def fromThing(t: Thing): (String, Option[Int], Option[String]) = ??? // elided
def * = (id, intValue, stringValue) <> ((toThing _).tupled, fromThing)
}
This is not compling:
[error] found : [U]slick.lifted.MappedProjection[Thing,U]
[error] required: slick.lifted.ProvenShape[Thing]
[error] ) <> ((toThing _).tupled, fromThing _)
Am I approaching this the wrong way? What's the idiomatic way of representing an ADT?
The problem is in signature of methods toThing and fromThing.
It should look so:
sealed trait OrValue
case class IntValue(value: Int)
case class StringValue(value: String)
case class Thing(id: Int, value: OrValue)
class Things(tag: Tag) extends Table[Thing](tag, "things") {
def id = column[Int]("id", O.PrimaryKey)
def intValue = column[Option[Int]]("intValue")
def stringValue = column[Option[String]]("stringValue")
def toThing(source: (Int, Option[Int], Option[String])): Thing = source match {
case (id, intValue, stringValue) => ??? //TODO implement
}
def fromThing(t: Thing): Option[(Int, Option[Int], Option[String])] = ??? //TODO implement
def * = (id, intValue, stringValue) <> (toThing _, fromThing _)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With