简体   繁体   English

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

[英]Is it possible to find a common supertype on type-system level in Scala?

Is it possible to make a type-alias (or something equivalent) in Scala that takes two parameters and returns their common supertype? 是否有可能在Scala中创建一个类型别名(或类似的东西),它接受两个参数并返回它们的常见超类型? In other words, I'm trying to find something with this signature: 换句话说,我正在尝试使用此签名找到一些东西:

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

where these hold true: (pseudocode) 这些都适用:(伪代码)

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

etc. 等等

I haven't been able to find it myself, but I can't use alternatives like adding an extra parameter, since I have to follow a pre-specified interface. 我自己无法找到它,但我不能使用其他替代参数,因为我必须遵循预先指定的接口。

(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. Scala的一个令人印象深刻的功能是它能够在将橙色附加到苹果列表时返回水果列表。 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 . 无论如何,虽然我喜欢这种类型的问题(关于类型的问题),但在这里它闻起来像一个XY问题

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

This is not my idea, it's actually adapted ever so slightly from this post by Miles Sabin . 这不是我的想法,它实际上从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. 我所做的唯一改变是,虽然他使用¬¬[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]
}

Gives us: 给我们:

Main.Pet
Any

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? ) 您可以获取最不常见的超类型的typeTag,然后提取其类型(请参阅如何从TypeTag [T]或scala中的任何其他泛型中捕获T?

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? 对于isInstanceOf,请参阅如何知道对象是否是TypeTag类型的实例?

The only restriction is that you can't obtain common supertype of type parameters - it will always be erased to Any. 唯一的限制是你不能获得常见的类型参数超类型 - 它将永远被删除为任何。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM