Можно ли найти общий супертип на уровне системы типов в Scala? - PullRequest
8 голосов
/ 05 ноября 2011

Можно ли создать псевдоним типа (или что-то эквивалентное) в Scala, который принимает два параметра и возвращает их общий супертип? Другими словами, я пытаюсь найти что-то с этой подписью:

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

где они верны: (псевдокод)

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

и т.д.

Я не смог найти его сам, но я не могу использовать альтернативы, такие как добавление дополнительного параметра, поскольку я должен следовать предварительно заданному интерфейсу.

Ответы [ 3 ]

3 голосов
/ 11 ноября 2011

(не полное решение, но может дать некоторые идеи)

Одна впечатляющая особенность 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

Следующим шагом будет поднять этот трюк на более высокийродственные типы (не проверено).
Полусгоревшее решение состоит в создании значений из типов (ср. этот ответ ):

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 Проблема .

2 голосов
/ 10 декабря 2014

Я могу дать вам черту CommonSupertype[A, B] и неявную функцию генератора, так что вы можете просто потребовать неявный экземпляр этой черты там, где она вам нужна, и она будет содержать общий супертип (как зависимый тип).

Это не моя идея, на самом деле она немного адаптирована из этого поста Майлза Сабина .

Единственное изменение, которое я сделал, заключается в том, что, хотя он использует ¬¬[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
1 голос
/ 26 сентября 2014

Вы можете получить typeTag наименее распространенного супертипа, а затем извлечь его тип (см. Как получить T из TypeTag [T] или любого другого универсального в 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 см. Как узнать, является ли объект экземпляром типа TypeTag?

Единственное ограничение - вы не можете получить общеесупертип параметров типа - он всегда будет удален в Any.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...