In this Scala 2.13 example:
import scala.language.implicitConversions
object Main extends App {
trait Choosable {
type T
type Choose = this.type => T
@inline implicit def choice(choose: Choose): T = choose(this)
}
object Choosable {
implicit class ChooseOps[C <: Choosable](private val choose: C#Choose) extends AnyVal {
def display(implicit choice: C#Choose => C#T): String = choice(choose).toString
}
}
object Color extends Choosable {
override type T = String
val Red: T = "Red"
val Blue: T = "Blue"
}
def displayChosenColor(color: Color.Choose): Unit = {
println(Choosable.ChooseOps[Color.type](color).display(Color.choice))
}
displayChosenColor(_.Red)
}
It fails with:
type mismatch;
found : Main.Color.Choose => Main.Color.T
(which expands to) (Main.Color.type => String) => String
required: (_1.type => _1.T forSome { val _1: Main.Color.type }) => Main.Color.T
(which expands to) (Function1[_ <: Main.Color.type with Singleton, String]) => String
println(Choosable.ChooseOps[Color.type](color).display(Color.choice))
Why is an existential type?
Why is that existential type expanding to (Function1[_ <: Main.Color.type with Singleton, String]) => String?
Is there a way too get display to require a Main.Color.Choose => Main.Color.T?
This Q&A (Scala trait `this.type` in type parameter) gives some clues that it might have something to do with this.type.
I get that this.type is the Singleton type which denotes the set containing only this.
In my case, C is Color.type which denotes the set containing only Color.
We can see from the error that Color.choice has the type (Main.Color.type => String) => String.
The thing that I don't get is the existential type _1.type => _1.T forSome { val _1: Main.Color.type }. Why is that not the same as Main.Color.type => String?