是否有可能在Scala中找到类型系统级别的常见超类型?

时间:2011-11-04 20:24:08

标签: scala types

是否可以在Scala中创建一个带有两个参数并返回其常用超类型的类型别名(或类似的东西)?换句话说,我正在尝试用这个签名找到一些东西:

type CommonSupertype[A, B] // can't be more specific

这些都适用:(伪代码)

CommonSupertype[String, Int] = Any
CommonSupertype[JButton, JPanel] = JComponent

我自己无法找到它,但我无法使用其他替代参数,因为我必须遵循预先指定的界面。

3 个答案:

答案 0 :(得分:3)

(不是一个完整的解决方案,但可能会提供一些想法)

Scala的一个令人印象深刻的功能是它能够在将橙色附加到苹果列表时返回水果列表。 它的值很好,正是因为你推断了泛型类型。

import scala.reflect.Manifest

def CommonSuperType[A, B >: A : Manifest](a:A, b:B) = manifest[B]  

它有效(种类):

scala> CommonSuperType(new JButton, new JPanel)
res42: Manifest[javax.swing.JComponent with javax.accessibility.Accessible] = javax.swing.JComponent with javax.accessibility.Accessible

下一步是将这个技巧提升到更高级别的类型(未经测试) 半成品解决方案包括从类型创建值(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)
}   

但我陷入了这种不直观的不一致之处:

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

无论如何,虽然我喜欢这类问题(关于类型的问题),但这里有点像XY Problem

答案 1 :(得分:2)

我可以给你一个特征CommonSupertype[A, B]和一个隐式生成器函数,所以你可以在需要的地方只需要这个特性的隐式实例,它将包含公共超类型(作为依赖类型)。

这不是我的想法,它实际上从this post by Miles Sabin稍微适应了。

我所做的唯一改变是,虽然他使用¬¬[C] <:< (A ∨ B)作为证据表明类型C是A或B的子类型,但我已经颠倒了子类型方向(所以:{{1检查A和B都是C的子类型。

(A ∨ B) <:< ¬¬[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]
}

答案 2 :(得分:1)

您可以获取最不常见的超类型的typeTag,然后提取其类型(参见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
}

用法

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

对于isInstanceOf,请参阅How to know if an object is an instance of a TypeTag's type?

唯一的限制是您无法获得常见的类型参数超类型 - 它将始终被删除为任何。