Объединение внутренних классов в разных родительских экземплярах - PullRequest
0 голосов
/ 26 сентября 2018

РЕДАКТИРОВАТЬ: Добавлено в информации о F[_]

Вот настройки.У нас есть родительский класс, который содержит тип, и метод, который обрабатывает проекцию типа этого во всех родительских экземплярах:

class Parent[F[_]] {

  // These depend on information from Parent (ie F) so
  // cannot be moved outside
  trait Inner { def execute[A]: F[A] }
  case class Foo(i: Int) extends Inner { ... }
  case class Bar(s: String) extends Inner { ... }

  def process(value: Parent#Inner): Unit = value match {
    case Foo(_) => println("integer")
    case Bar(_) => println("string")
  }

}

Проблема заключается в том, что внутри соответствия регистров ссылки на Foo иBar являются this.Inner, а не Parent#Inner.Таким образом, происходит следующее:

val foo = (new Parent[IO]).Foo(5)
val processer = new Parent[IO]
processer.process(foo)  // match error

Одним из способов решения этой проблемы является изменение def process на:

  def process(value: Parent#Inner): Unit = value.asInstanceOf[this.Inner] match {
    case Foo(_) => println("integer")
    case Bar(_) => println("string")
  }

(обратите внимание на новый .asInstanceOf).

Однако это неудовлетворительно.

Помимо извлечения def process в сторонний класс, есть ли лучший способ достичь желаемого поведения?

РЕДАКТИРОВАТЬ:

Классы, к сожалению, должны быть определены внутри Parent из-за зависимости от F[_].Теоретически мы могли бы переместить их наружу, как предполагает первоначальный ответ, но это повлекло бы за собой слишком много работы и разнообразия в других местах, поскольку нам нужно было бы параметризовать каждый Inner подкласс с помощью F[_]

EDIT 2:

Одним из возможных решений является переформулировка process следующим образом:

def process(value: Parent#Inner): Unit = value match {
  case _: Parent[F]#Foo => println("integer")
  case _: Parent[F]#Bar => println("string")
}

Но это означает, что мы не можем использовать метод неприменения Foo.Недопустимо следующее:

case Parent[F]#Foo(_) => println("integer")

В случае, когда вместо Foo указано, например, Foo[A, B, C](a: A, b: B, c: C), это будет означать, что оператором соответствия становится:

case _: Parent[F]#Foo[A, B, C] @unchecked => ...

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

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

Вы должны писать либо с зависимыми от пути типами

class Parent[F[_]] {
  trait Inner {
    def execute[A]: F[A]
  }

  case class Foo(i: Int) extends Inner {
    override def execute[A]: F[A] = ???
  }

  case class Bar(s: String) extends Inner {
    override def execute[A]: F[A] = ???
  }

  def process(value: Inner): Unit = value match {
    case Foo(_) => println("integer")
    case Bar(_) => println("string")
  }
}

val processer = new Parent[IO]
val foo: processer.Inner = processer.Foo(5)
processer.process(foo)

, либо с проекциями типов

class Parent[F[_]] {
  trait Inner {
    def execute[A]: F[A]
  }

  case class Foo(i: Int) extends Inner {
    override def execute[A]: F[A] = ???
  }

  case class Bar(s: String) extends Inner {
    override def execute[A]: F[A] = ???
  }

  def process(value: Parent[F]#Inner): Unit = value match {
    case _: Parent[F]#Foo => println("integer")
    case _: Parent[F]#Bar => println("string")
  }
}

val foo: Parent[IO]#Inner = new Parent[IO].Foo(5)
val processer = new Parent[IO]
processer.process(foo) 

Пример с параметризованным Foo без непроверенного соответствия типов:

class Parent[F[_]] {
  trait Inner {
    def execute[A]: F[A]
  }

  case class Foo[B](i: Int) extends Inner {
    override def execute[A]: F[A] = ???
  }

  case class Bar(s: String) extends Inner {
    override def execute[A]: F[A] = ???
  }

  def process(value: Parent[F]#Inner): Unit = value match {
    case _: Parent[F]#Foo[_] => println("integer")
    case _: Parent[F]#Bar => println("string")
  }
}
0 голосов
/ 26 сентября 2018

Вы можете использовать проекции типов в сопоставлении с образцом:

def process(value: Parent[F]#Inner): Unit = value match {
  case _: Parent[F]#Foo => println("integer")
  case _: Parent[F]#Bar => println("string")
}

Другой подход, который позволяет использовать unapply:

// Start writing your ScalaFiddle code here
class Parent[F[_]] { self =>

  // These depend on information from Parent (ie F) so
  // cannot be moved outside
  trait Inner { def parent = self }
  case class Foo(i: Int) extends Inner
  case class Bar(s: String) extends Inner

  def process(value: Parent[F]#Inner): Unit = {
    val parent = value.parent
    value match {
      case parent.Foo(_) => println("integer")
      case parent.Bar(_) => println("string")
    }
  }

}

val foo = (new Parent[List]).Foo(5)
val processer = new Parent[List]
processer.process(foo) // integer

Вы также можете использовать value.parent.Foo(_) какшаблон, если вы делаете Inner#parent a val.

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