Как избежать дублирования привязанного типа в Scala

У меня есть класс с параметром ограниченного типа.

Я пробовал с identity, но возвращаемый тип неточен.

И в методе identityTP мне нужно снова указать ограничение

Как избежать дублирования этого ограничения с методами, работающими с этим типом?

Вот пример:

  sealed trait Location
  case object Single extends Location
  case object Multi extends Location

  final case class Log[L <: Location](l: L)

  def identity(log: Log[_]): Log[_] = log
  def identityTP[L<: Location](log: Log[L]): Log[L] = log

person Yann Moisan    schedule 04.07.2020    source источник
comment
Что именно ты хочешь делать?   -  person Luis Miguel Mejía Suárez    schedule 04.07.2020


Ответы (1)


Собственно это не дублирование. L дюйм

final case class Log[L <: Location](l: L)

и L в

def identityTP[L <: Location](log: Log[L]): Log[L] = log

- это два совершенно разных параметра типа. Это было бы более понятно, если бы вы использовали разные идентификаторы.

final case class Log[L <: Location](l: L)
def identityTP[L1 <: Location](log: Log[L1]): Log[L1] = log

Верхняя граница для одного параметра типа не дублирует верхнюю границу для другого параметра типа.

Также это не дублирование, потому что на самом деле верхние границы могут быть разными.

sealed trait SubLocation extends Location
final case class Log[L <: Location](l: L)
def identityTP[L1 <: SubLocation](log: Log[L1]): Log[L1] = log

Если вы не хотите создавать параметр второго типа, вы можете сделать identityTP вложенным в Log (сделав его методом Log)

final case class Log[L <: Location](l: L) {
  def identityTP: Log[L] = this
}

Иногда может помочь, если вы сделаете L членом типа, а не параметром типа.

trait Log {
  type L <: Location
  val l: L
}
object Log {
//  def apply[_L <: Location](_l: _L): Log { type L = _L} = new Log {
//    override type L = _L
//    override val l: L = _l
//  }

  def apply[_L <: Location](_l: _L): Log = new Log {
    override type L = _L
    override val l: L = _l
  }
}

//  def identityTP(log: Log): Log = log
def identityTP(log: Log): Log { type L = log.L } = log

Обратите внимание, что хотя мы должны повторить верхнюю границу в apply, но не в identityTP.

Обычно повторение верхней границы при необходимости не представляет большого труда.

class MyClass[A <: A1]
def foo[A <: A1](mc: MyClass[A]) = ???
def bar[A <: A1](mc: MyClass[A]) = ???

Когда это становится громоздким

class MyClass[A <: A1, B <: B1, C <: C1]
def foo[A <: A1, B <: B1, C <: C1](mc: MyClass[A, B, C]) = ???
def bar[A <: A1, B <: B1, C <: C1](mc: MyClass[A, B, C]) = ???

вам следует изменить дизайн своих абстракций. Например

trait Tuple {
  type A <: A1
  type B <: B1
  type C <: C1
}
class MyClass[T <: Tuple]
def foo[T <: Tuple](mc: MyClass[T]) = {
  //T#A, T#B, T#C instead of A, B, C
  ???
}

or

class MyClass[T <: Tuple](val t: T)
//class MyClass(val t: Tuple)

def foo[T <: Tuple](mc: MyClass[T]) = {
//def foo(mc: MyClass) = {
  import mc.t
  //t.A, t.B, t.C instead of A, B, C
  ???
}

Также иногда вы можете поиграть с заменой границ типа ограничениями типа

final case class Log[L](l: L)(implicit ev: L <:< Location)
def identityTP[L](log: Log[L])(implicit ev: L <:< Location): Log[L] = log

Хотя это не удаляет повторы, но есть способы бороться с повторениями и среди неявных параметров (классов типов). См. Как обернуть метод, имеющий неявно с другим методом в Scala?

person Dmytro Mitin    schedule 04.07.2020