The ValueOf
type function can be used in the contextual parameters list of a method to pick the single inhabitant of singleton type.
enum Color { case red, green, blue }
def singleInhabitantOf[T](using holder: ValueOf[T]): T = holder.value
println(singleInhabitantOf[Color.red.type] == Color.red) // outputs true
But it is limited. It doesn’t work with tuples whose component types are all singletons.
singleInhabitantOf[(Color.red.type, Color.blue.type)] // compile error
The error message is: No singleton value available for (Color.red, Color.blue); eligible singleton types for ValueOf
synthesis include literals and stable paths.
So I tried to create a version for tuples this way:
import scala.Tuple
type ValuesOf[T <: Tuple] <: Tuple = T match {
case EmptyTuple => EmptyTuple
case h *: t => ValueOf[h] *: ValuesOf[t]
}
def singleInhabitantsOf[T<:Tuple](using holder: ValuesOf[T]): Tuple = holder // a tuple mapping is missing here but how to implement it is another question.
singleInhabitantsOf[(Color.red.type, Color.blue.type)] // Copile error
Unfortunately, the compiler complains saying: No given instance of type ValuesOf[(Color.red, Color.blue)] was found for parameter holder.
Apparently it looks for a tuple instance in the implicit context instead of synthetizing a tuple with the singletons instances.
However
implicitly[summonAll[ValuesOf[T]]]
compiles and runs fine.
So I could rewrite the method using summonAll
like this
inline def singleInhabitantsOf2[T <: Tuple]: Tuple = summonAll[ValuesOf[T]]
But this solution is not useful for my final purpose, for which the check of singletonness should be in the method signature, such that the compiler don’t start processing the body if something is wrong.