I am just trying things out in scala and I wrote this code
object Main:
def main(args: Array[String]): Unit =
val dInt: Data[Int] = IntData(1)
val dString: Data[String] = StringData("hello")
val Data(deconstructedInt) = dInt // unapply
val Data(deconstructedString) = dString // unapply
println(deconstructedInt)
println(deconstructedString)
sealed trait Data[ T]:
def get: T
case class IntData(get: Int) extends Data[Int]
case class StringData(get: String) extends Data[String]
object Data:
def apply[T](input: T): Data[T] = input match {
case i: Int => IntData(i) //compile error here
case s: String => StringData(s) //compile error here
}
def unapply[T](d: Data[T]): Option[String] = d match {
case IntData(get) => Some(s"int data => get = $get")
case StringData(get) => Some(s"string data => get = $get")
}
I get at the location commented in the code this error
Found: IntData
Required: Data[T]
case i: Int => IntData(i)
why I am getting this error, isn't IntData
(or StringData
) a subclass of Data
?
CodePudding user response:
IntData
is a subtype of Data[Int]
. So if T
is not Int
, IntData
is not a subtype of Data[T]
. Now, you might say, if input
matches the first case, then clearly T
is Int
. But the compiler is not smart to realise this!
You could try using Scala 3's new match types:
type DataOf[T] = T match {
case Int => IntData
case String => StringData
}
def apply[T](input: T): DataOf[T] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
Another alternative is union types:
def apply(input: Int | String): Data[Int | String] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
However, you will lose type information in doing this. Using the match types solution, apply(42)
is inferred to have type IntData
. Using the union types solution, it is inferred to have type Data[Int | String]
.
CodePudding user response:
This way compiler connects the dots between T
s and it works:
object Main:
def main(args: Array[String]): Unit =
val dInt: Data[Int] = IntData(1)
val dString: Data[String] = StringData("hello")
val Data(deconstructedInt) = dInt // unapply
val Data(deconstructedString) = dString // unapply
println(deconstructedInt)
println(deconstructedString)
sealed trait Data[ T]:
def get: T
case class IntData[T <: Int](get: T) extends Data[T]
case class StringData[T <: String](get: T) extends Data[T]
object Data:
def apply[T](input: T): Data[T] = input match {
case i: Int => IntData(i)
case s: String => StringData(s)
}
def unapply[T](d: Data[T]): Option[String] = d match {
case IntData(get) => Some(s"int data => get = $get")
case StringData(get) => Some(s"string data => get = $get")
}