【问题标题】:Is it possible to find a common supertype on type-system level in Scala?是否可以在 Scala 中找到类型系统级别的通用超类型?
【发布时间】:2011-11-04 20:24:08
【问题描述】:

是否可以在 Scala 中创建一个类型别名(或等效的东西),它接受两个参数并返回它们的公共超类型?换句话说,我正试图找到带有这个签名的东西:

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

这些都成立:(伪代码)

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

等等

我自己找不到,但我不能使用其他方法,比如添加额外的参数,因为我必须遵循预先指定的界面。

【问题讨论】:

  • 给我们一个带有示例代码的用例,以便我们解决问题。
  • 这是不可能通过类型别名实现的,但在上下文中,您可以做一些具有类似效果的事情......所以我回应@Jean-PhilippePellet 询问一个或两个用例.
  • 是的:type CommonSupertype[A, B] = Any。但我认为您可能正在寻找最少常见的超类型......
  • 两种类型不一定有一个最不常见的超类型(想想多个特征)。或者那会是最常见的超类型吗?
  • 用例示例很复杂(很多文件),所以我必须简化一下。正在努力。 @michid 我的意思是最不常见的超类型。

标签: scala types


【解决方案1】:

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

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

下一步是将这个技巧提升到更高种类的类型(未测试)。
一个半生不熟的解决方案包括从类型创建值(参见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

【讨论】:

    【解决方案2】:

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

    这不是我的主意,它实际上是从this post by Miles Sabin 改编而来的。

    我所做的唯一更改是,虽然他使用 ¬¬[C] <:< (A ∨ B) 作为类型 C 是 A 或 B 的子类型的证据,但我已经将子类型方向颠倒了(例如:(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]
    }
    

    给我们:

    Main.Pet
    Any
    

    【讨论】:

      【解决方案3】:

      您可以获取最不常见超类型的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?

      唯一的限制是你不能获得类型参数的通用超类型——它总是会被擦除为 Any。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-29
        • 2011-03-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多