Scala: использование Nothing для единичных экземпляров полиморфных типов - PullRequest
2 голосов
/ 03 октября 2011

Учитывая полиморфную черту, такую ​​как

 trait Transform[T] { def apply( t: T ) : T }

, можно реализовать различные специализированные экземпляры, такие как

 case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... }
 case class Append[String] extends Transform[String] { def apply( t: String ) ... }

и т. Д.Теперь часто желаемое преобразование также является преобразованием идентичности.Вместо того, чтобы специализировать идентичность для каждого типа T, представляется предпочтительным использовать только один экземпляр для всех типов T. Мой вопрос: каков наилучший способ сделать это в Scala?

Вот что я нашел такдалеко: глядя на то, как List [T] реализует List.empty [T] и Nil, я попытался использовать Nothing в качестве типа T. Это, кажется, имеет смысл, поскольку Nothing является подтипом любого другого типа:

 object Identity extends Transform[Nothing] {
    def apply( t: Nothing ) = t
 }

Кажется, это работает.Однако, где бы я ни захотел использовать этот экземпляр как есть, как здесь:

 val array = Array[Transform[String]]( Transform.Identity )

Я получаю ошибку компилятора "несоответствие типов; найдено: Identity.type, required: Transform [String]".Чтобы использовать его, я должен явным образом разыграть его:

 ... Identity.asInstanceOf[Transform[String]]

Я не уверен, что это лучший или даже «правильный» способ сделать это.Спасибо за любой совет.

Ответы [ 2 ]

5 голосов
/ 03 октября 2011

Как @Kim Stebel указывает, что Transform[T] является инвариантом в T (и должно быть потому, что T встречается как в ко-, так и в противоположных позициях в def apply(t : T) : T), поэтому Transform[Nothing] не является подтипомTransform[String] и не может быть задано.

Если ваша главная задача - создание экземпляра при каждом вызове def Id[A] Кима, тогда ваша лучшая модель - это определение conforms в Predef,

private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

т. Е. Использовать полиморфный метод, возвращая одноэлементное значение, приведенное к соответствующему типу. Это один из случаев, когда стирание является победой.

Применительно к вашей ситуации мы получим,

object SingletonId extends Transform[Any] { def apply(t : Any) = t }
def Id[A] = SingletonId.asInstanceOf[Transform[A]]

Пример сеанса REPL,

scala> Id("foo")
res0: java.lang.String = foo

scala> Id(23)
res1: Int = 23
4 голосов
/ 03 октября 2011

Поскольку параметр типа T в Transform[T] является инвариантным, Transform[Nothing] не является подтипом Transform[String], поэтому компилятор жалуется на это. Но использование Nothing здесь все равно не имеет смысла, поскольку никогда не может быть экземпляра Nothing. Итак, как бы вы передали один метод apply? Тебе нужно будет разыграть еще раз. Единственный вариант, который я вижу, это:

scala> def Id[A] = new Transform[A] { override def apply(t:A) = t }
Id: [A]=> java.lang.Object with Transform[A]

scala> Id(4)
res0: Int = 4

scala> Id("")
res1: java.lang.String = ""
...