Принуждение ценности бесформенной записи - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть обертка вокруг бесформенной записи.

Я хочу извлечь значение из этой записи и доказать, что это экземпляр полиморфного типа, например List[_]

import shapeless._
import shapeless.record._
import shapeless.ops.record._
import shapeless.syntax.singleton._

case class All[L <: HList](containers: L) {


  def getValue[Value, A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, Value],
                         equiv: Value =:= List[A]
                        ): List[A] =
    equiv.apply(containers.get(containerKey))

}

Прямо сейчас я могу позвонить getValue, если я явно укажу параметры типа Value и A, но, поскольку я работаю с гораздо более сложными типами, чем List, мне действительно нужны эти параметры типа длябыть выведен.

val all = All(
  'x ->> List[Int](1, 2, 3) ::
  'y ->> List[String]("a", "b") ::
  'z ->> 90
  HNil
)

// doesn't compile: Cannot prove that Value =:= List[A].
all.getValue('x)

// compiles
all.getValue[List[Int], Int]('x)

Есть ли способ извлечь значение, принудительно привести его к, например, List[_], при этом не нужно указывать какие-либо параметры типа?

Обратите внимание, что эта стратегия работает полностьюхорошо, если я хочу доказать, что значение является простым мономорфным типом, например Value =:= Int, но не для Value =:= List[A]

1 Ответ

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

Есть как минимум два способа сделать это, и оба включают изменение подписи getValue.

Во-первых, вы можете просто использовать ограниченный универсальный параметр:

  def getValue[R <: List[_]](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, R]): R =
    containers.get(containerKey)

Обратите внимание, что поскольку мы используем R в качестве возвращаемого типа, информация о результате во время компиляции не будет потеряна.Вы просто не сможете вызвать эту функцию, если значение в containerKey не является списком.


Во-вторых, вы можете использовать границы подтипа.Я понятия не имею, почему это работает, правда.Я подозреваю, что использование слишком строгих ограничений приводит к тому, что компилятор отклоняет некоторые решения, использующие типы уточнения.Это работает как в случае замены параметра типа в Selector на ограниченный подстановочный знак:

  def getValue[A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, _ <: List[A]]): List[A] =
    containers.get(containerKey)

, так и в случае использования доказательства подтипа <:< вместо равенства =:=:

  def getValue[Value, A](containerKey: Witness)
                        (implicit sel: Selector.Aux[L, containerKey.T, Value],
                         equiv: Value <:< List[A]
                        ): List[A] =
    equiv.apply(containers.get(containerKey))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...