Как правильно предоставить доказательства конструктору класса case, который зависит от класса аргумента - PullRequest
0 голосов
/ 16 января 2019

Имею тип Conjuction

type ![S] = S => Nothing
type !![S] = ![![S]]
type ∨[T, U] = ![![T] with ![U]]
type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

Иерархия классов: a, b, c, d, n

abstract class State
case class A() extends State
case class B() extends State
case class N() extends State

// what should i place in ??? , state.type doesn't compile : 
// Error: not found value state ... in evidence
case class C(state: State)(implicit ev: (A |∨| B)#λ[???]) 
case class D(state: State)(implicit ev: (A |∨| B |∨| C)#λ[???])

выборок:

val a = A(); val b = B(); val n = N()
val ca = C(a); val cb = C(b)
//this shouldn't compile because evidence (A |∨| B)
val cn = C(n)

Какправильно использовать доказательства ??

1 Ответ

0 голосов
/ 16 января 2019

Попробуйте параметризовать классы дел:

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  case class C[S <: State](state: S)(implicit ev: (A |∨| B)#λ[S])

  val a = A()
  val b = B()
  val n = N()

  val ca = C(a)
  val cb = C(b)
  // val cn = C(n) // doesn't compile

Кодировка для большего количества классов

  type ![S] = S => Nothing
  type !![S] = ![![S]]

  trait Disj[T] {
    type or[S] = Disj[T with ![S]]
    type apply = ![T]
  }

  // for convenience
  type disj[T] = { type or[S] = Disj[![T]]#or[S] }

  type w[T, U, V] = disj[T]#or[U]#or[V]#apply
  type ww[T, U, V] = {type λ[X] = !![X] <:< w[T, U, V]}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class C() extends State
  case class N() extends State

  case class D[S <: State](state: S)(implicit ev: ww[A, B, C]#λ[S])

  val a = A()
  val b = B()
  val c = C()
  val n = N()

  val da = D(a)
  val db = D(b)
  val dc = D(c)
//  val dn = D(n) // doesn't compile

Или вы можете использовать apply метод

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  class C private(state: State)
  object C {
    def apply(state: State)(implicit ev: (A |∨| B)#λ[state.type]) = new C(state)
  }

  val a = A()
  val b = B()
  val n = N()

  val ca = C(a)
  val cb = C(b)
//  val cn = C(n) // doesn't compile

Если вы хотите избежать сопутствующих объектов с помощью метода apply (по некоторым причинам), вы можете рассмотреть вторичные конструкторы

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  // "ignored" is to avoid constructor ambiguity
  class C private(state: State, ignored: Int) {
    def this(state: State)(implicit ev: (A |∨| B)#λ[state.type]) = this(state, 0)
  }

  val a = A()
  val b = B()
  val n = N()

  val ca = new C(a)
  val cb = new C(b)
//  val cn = new C(n) // doesn't compile

Использование вторичного конструктора или сопутствующего объекта apply - это стандартный обходной путь для обработки такого рода зависимостей в конструкторах ( 1 2 3 ). Альтернатива состоит в том, чтобы дождаться реальных типов объединения в Dotty (текущий подход - их частичная эмуляция) или разрешить SI-5712 в Scala.

Для автоматической генерации сопутствующих объектов вы можете рассмотреть макросы или генерацию кода.

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