The type class Type should handle container types and their element types. The container type is given as type parameter, whereas the element type is given as abstract type.
This seems to work as long as the type of the summoned type class is inferred. Once the type for the summoned type class is explicitly defined it doesn’t compile anymore, see last line in the code example.
What is wrong with this code and how could I fix it?
import org.scalatest.freespec.AnyFreeSpec
trait Type[A]:
type B
def convert(value: String): B
def collect(values: Seq[B]): A
given tp: Type[Option[Int]] with
type B = Int
def convert(value: String): Int = value.toInt
def collect(values: Seq[Int]): Option[Int] = values.lastOption
class TypeTest extends AnyFreeSpec:
"inferred type" in:
val t = summon[Type[Option[Int]]]
val a: Int = t.convert("7")
"explicit type" in:
val t: Type[Option[Int]] = summon[Type[Option[Int]]]
val a: Int = t.convert("7")
Yes, in the explicit type you are explicitly erasing and forgetting what B is.
You could either be fully explicit Type[Option[Int]] { type B = Int } or use an Aux pattern to simplify that.
Is this not exactly what is done in the given instance creation?
given tp: Type[Option[Int]] with
type B = Int
def convert(value: String): Int = value.toInt
def collect(values: Seq[Int]): Option[Int] = values.lastOption
The given isn’t the problem, it’s the summon[T] which will return a value of type T. When you wrote summon[Type[Option[Int]]] you lost the knowledge of the abstract type.
They, dont. That is what I just told you.
The one being inferred is more precise than the one you are typing. If you want both to be the same, you have to be fully precise.
Sure, but again you are erasing that when providing the explicit type.
For example, 1 has the literal type 1 but when you do val x: Int = 1 you erase that knowledge of a more precise type and leave it just as an Int