Вернуть наиболее определенный тип, заданный параметром метода - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь создать определенный и уникальный тип, заданный некоторым переменным параметром.

В приведенном ниже списке вы найдете предопределенные типы, представляющие первые четыре натуральных числа (с HList-подобным подходом Digit0..Digit9, DimensionCons и EndDimension (например, HNil)).

  object Defined {
    type D1 = DimensionCons[Digit1, EndDimension.type]
    val d1 = DimensionCons(Digit1(), EndDimension)

    type D2 = DimensionCons[Digit2, EndDimension.type]
    val d2 = DimensionCons(Digit2(), EndDimension)

    type D3 = DimensionCons[Digit3, EndDimension.type]
    val d3 = DimensionCons(Digit3(), EndDimension)

    type D4 = DimensionCons[Digit4, EndDimension.type]
    val d4 = DimensionCons(Digit4(), EndDimension)
  }

Я ищу метод с указанной подписью, такой как

  def getDimensionTpe[D <: Dimension](dim: Int) : D

что например возвращает DimensionCons[Digit2, EndDimension.type] для getDimensionTpe(2).

Вопросы:

  • Возможно ли это без макроса whitebox (который генерирует типы?) Если да, то как и о чем заботиться?
  • Возможны или применимы альтернативные методы?
  • Могут ли помочь зависимые от пути типы?

Спасибо, Мартин

1 Ответ

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

Я не уверен, что понимаю, что именно вы делаете с DimensionCons и бизнесом DigitN, но можно использовать Shapeless для написания метода, который будет принимать целое число (хотя только литеральная константа - оно должно быть известно во время компиляции) и иметь это целое число, определяющее статический тип возвращаемого значения.

Чтобы упростить ваш код ради полного рабочего примера, предположим, что мы хотим написать метод, подобный следующему:

trait Dimension
case class Dim1() extends Dimension
case class Dim2() extends Dimension
case class Dim3() extends Dimension
// And so on...

def getDimensionTpe[D <: Dimension](dim: Int) : D

… где getDimensionTpe(1) вернет Dim1() (статически набрано Dim1), getDimensionTpe(2) вернет Dim2() и т. Д. Для этого мы можем ввести отображение класса типов между натуральными числами без формы и измерениями:

import shapeless.{ DepFn0, Nat }

trait DimMap[N <: Nat] extends DepFn0 {
  type Out <: Dimension
}

object DimMap {
  type Aux[N <: Nat, D <: Dimension] = DimMap[N] { type Out = D }

  implicit val dimMap1: Aux[Nat._1, Dim1] = new DimMap[Nat._1] {
    type Out = Dim1
    def apply(): Dim1 = Dim1()
  }

  implicit val dimMap2: Aux[Nat._2, Dim2] = new DimMap[Nat._2] {
    type Out = Dim2
    def apply(): Dim2 = Dim2()
  }

  implicit val dimMap3: Aux[Nat._3, Dim3] = new DimMap[Nat._3] {
    type Out = Dim3
    def apply(): Dim3 = Dim3()
  }

  // And so on as needed.
}

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

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

def getDimensionTpe[D <: Dimension](dim: Int) : D

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

def getDimensionTpe(dim: Nat)(implicit m: DimMap[dim.N]) : m.Out = m()

А потом:

scala> val d1: Dim1 = getDimensionTpe(1)
d1: Dim1 = Dim1()

scala> val d2: Dim2 = getDimensionTpe(2)
d2: Dim2 = Dim2()

scala> val d3: Dim3 = getDimensionTpe(3)
d3: Dim3 = Dim3()

Если вы неправильно указали статический тип, компилятор скажет вам:

scala> val d3: Dim1 = getDimensionTpe(3)
<console>:15: error: type mismatch;
 found   : DimMap.dimMap3.Out
    (which expands to)  Dim3
 required: Dim1
       val d3: Dim1 = getDimensionTpe(3)
                                     ^

Если вы предоставляете целочисленный литерал без отображения, это также ошибка времени компиляции:

scala> getDimensionTpe(0)
<console>:14: error: could not find implicit value for parameter m: DimMap[shapeless._0]
       getDimensionTpe(0)
                      ^

И, наконец, если вы предоставите аргумент Int, который не является целочисленным литералом, вы также получите ошибку компилятора:

scala> val x = 1
x: Int = 1

scala> getDimensionTpe(x)
<console>:16: error: Expression x does not evaluate to a non-negative Int literal
       getDimensionTpe(x)
                       ^

За кулисами Shapeless использует макрос, чтобы сделать это возможным, и даже это страшно - вы можете прочитать определение NatMacros для деталей.

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