Is it possible to make a type-alias (or something equivalent) in Scala that takes two parameters and returns their common supertype? In other words, I\'m trying to find some
I can give you a trait CommonSupertype[A, B]
and an implicit generator function, so you can just require an implicit instance of this trait wherever you need it, and it will contain the common supertype (as a dependent type).
This is not my idea, it's actually adapted ever so slightly from this post by Miles Sabin.
The only change I've made is that, while he is using ¬¬[C] <:< (A ∨ B)
as evidence that a type C is a subtype of either A or B, I've reversed the subtyping direction (so: (A ∨ B) <:< ¬¬[C]
) to check that both A and B are subtypes of C.
import scala.reflect.ClassTag
object Main extends App {
type ¬[A] = A => Nothing
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type ¬¬[A] = ¬[¬[A]]
trait CommonSupertype[A, B] {
type λ
def tag: ClassTag[λ]
}
// The ClassTag is only so I can get ahold of the type's name at runtime
implicit def commonSupertype[A, B, C : ClassTag](implicit C: (A ∨ B) <:< ¬¬[C]): CommonSupertype[A, B] { type λ = C } =
new CommonSupertype[A, B] { type λ = C; def tag = implicitly[ClassTag[C]] }
trait Pet
class Dog extends Pet
class Cat extends Pet
def check[A, B](implicit x: CommonSupertype[A, B]) = {
// This just prints the common type, but you could use the type (x.λ) directly
println(x.tag.toString())
}
check[Dog, Cat]
check[Dog, Double]
}
Gives us:
Main.Pet
Any
(Not a complete solution, but might give some ideas)
One impressive feature of Scala is its ability to return a list of Fruits when an orange is appended to a list of apples. It's fine with values, precisely because you let the generic type be inferred.
import scala.reflect.Manifest
def CommonSuperType[A, B >: A : Manifest](a:A, b:B) = manifest[B]
It works (kind of) :
scala> CommonSuperType(new JButton, new JPanel)
res42: Manifest[javax.swing.JComponent with javax.accessibility.Accessible] = javax.swing.JComponent with javax.accessibility.Accessible
Next step would be to lift this trick to higher kinded types (not tested).
An half baked solution consists in creating values from types (cf this answer) :
class CommonSuper[A:Manifest, B:Manifest] {
def make[T:Manifest] = manifest[T].erasure.newInstance.asInstanceOf[T]
val instanceA = make[A]
val instanceB = make[B]
def getType = CommonSuperType(instanceA, instanceB)
}
But I'm stuck in this unintuitive inconsistency :
scala> val test = new CommonSuper[JButton, JPanel]
scala> test.getType
res66: Manifest[Any] = Any
scala> CommonSuperType(test.instanceA, test.instanceB)
res67: Manifest[javax.swing.JComponent with javax.accessibility.Accessible] = javax.swing.JComponent with javax.accessibility.Accessible
Anyway, whereas I'm fond of this type of questions (questions about types), here it smells like an XY Problem.
You can obtain typeTag of least common supertype and then extract its type (see How to capture T from TypeTag[T] or any other generic in scala?)
import scala.reflect.runtime.universe._
import scala.util._
def t[A, B] = (null.asInstanceOf[A], null.asInstanceOf[B])
implicit class RichTuple[A: TypeTag](a: (A, A)) {def common = typeTag[A]}
implicit class RichT[T: TypeTag](a: T) {//just helper for working with typetags
def asInstanceOfT[U](t: TypeTag[U]) = a.asInstanceOf[U]
def hasSameTypeWith[U](t: TypeTag[U]) = typeTag[T] == t
}
Usage
scala> t[String, String].common
res87: reflect.runtime.universe.TypeTag[String] = TypeTag[String]
scala> t[String, Int].common
res88: reflect.runtime.universe.TypeTag[Any] = TypeTag[Any]
scala> ("aa" : String).hasSameTypeWith(t[String, String].common)
res105: Boolean = true
scala> ("aa" : String).hasSameTypeWith(t[String, Int].common)
res106: Boolean = false
scala> ("aa" : String).asInstanceOfT(t[String, Int].common)
res109: Any = aa
scala> ("aa" : String).asInstanceOfT(t[Int, Int].common)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
For isInstanceOf see How to know if an object is an instance of a TypeTag's type?
The only restriction is that you can't obtain common supertype of type parameters - it will always be erased to Any.