класс типов scala с более высокими типами и дисперсией - PullRequest
0 голосов
/ 30 июня 2018

У меня есть вопрос, очень похожий на этот: Скала дисперсия высшего рода

Это, однако, немного отличается тем, что не компилируется (scala 2.11.8).

Основная идея - взять предоставленный массив «вещей». Если массив равен нулю, вернуть значение по умолчанию некоторого типа (например, Boolean, Option, List[Int]), в противном случае выполнить работу с массивом и получить результат. Результат и значение по умолчанию имеют одинаковый тип.

Проблема, с которой я столкнулся, заключается в том, чтобы заставить ее работать с широким набором типов результатов.

Вот надуманный пример:

  trait NullGuard[F[_]] {
    def nullGuard[A, B](arr: Array[A], default: F[B])(expr: => F[B]): F[B] =
      if (arr == null || arr.length == 0) default else expr
  }

Давайте создадим реализацию, которая возвращает Option:

  implicit def optionNullGuard[F[X] <: Option[X]]: NullGuard[F] = new NullGuard[F]() {}

Выше скомпилировано, но следующая попытка использовать вышеупомянутый класс типов не делает:

  def returnsOption[F[_], A, B](arr: Array[A])(implicit ng: NullGuard[F]): Option[B] = {
    ng.nullGuard(arr, None) {
      // sample work
      if (arr.length % 2 == 0) Option(1) else None
    }
  }

Я получаю следующую ошибку компиляции:

type mismatch;
found   : None.type
required: F[?]
  ng.nullGuard(arr, None){

Как мне заставить это работать? Я также открыт для другого подхода, если он есть.

1 Ответ

0 голосов
/ 30 июня 2018

Поскольку в вашем классе типов нет абстрактных методов, его можно заменить одним полиморфным nullGuard методом:

def nullGuard[A, B]
  (arr: Array[A], defaultValue: B)
  (processArray: Array[A] => B)
: B = if (arr == null || arr.isEmpty) defaultValue else processArray(arr)

Более высокий параметр типа kinded F также, по-видимому, больше не нужен: вам не нужно ничего предоставлять метод, который работает для любого B в качестве возвращаемого типа, а не просто F[B].

Вот ваш надуманный, слегка измененный пример: извлечение последнего значения из массива, если у него четное число элементов:

for (example <- List[Array[Int]](null, Array(), Array(42), Array(1, 42))) {
  val lastAtEvenIndex = nullGuard[Int, Option[Int]](example, Some(0)) { 
    a => if (a.size % 2 == 0) Option(a.last) else None
  }
  println(lastAtEvenIndex)
}

Выход:

Some(0)
Some(0)
None
Some(42)

Возвращает None для массивов неравной длины и обрабатывает пустые / нулевые массивы, как если бы они имели 0 в качестве "последнего" элемента.


Полный пример в виде отдельного фрагмента кода с None в качестве значения по умолчанию:

def nullGuard[A, B]
  (arr: Array[A], defaultValue: B)
  (processArray: Array[A] => B)
: B = if (arr == null || arr.isEmpty) defaultValue else processArray(arr)


for (example <- List[Array[Int]](null, Array(), Array(42), Array(1, 42))) {
  val lastAtEvenIndex = nullGuard[Int, Option[Int]](example, None) { 
    a => if (a.size % 2 == 0) Option(a.last) else None
  }
  println(lastAtEvenIndex)
}

печать:

None
None
None
Some(42)
...