Scala: рекурсивное сопоставление с шаблоном гетерогенного списка, получение правильного типа каждого элемента - PullRequest
0 голосов
/ 03 июля 2018

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

import shapeless._
trait MyTrait {

  def myFunc[T](x: String => T): Boolean

  def iter(myHList: HList, acc: List[Boolean]): List[Boolean] = {
    myHList match {
      case HNil => acc
      case head :: tail => myFunc(head) :: iter(tail, acc)
    }
  }
}

Проблема в том, что голова, которую я получаю при сопоставлении, имеет тип Any, а не тип того, что я положил в HList. Я хочу, чтобы функция, принимающая аргумент head, имела правильный параметр типа T.

Возможно ли это? Может быть, с помощью других средств, кроме Shapeless?

1 Ответ

0 голосов
/ 06 июля 2018

Попробуйте

def iter(myHList: HList, acc: List[Boolean]): List[Boolean] = {
  (myHList: @unchecked) match {
    case HNil => acc
    case (head: Function1[String, _] @unchecked) :: tail => 
      myFunc(head) :: iter(tail, acc)
  }
}

(первое @unchecked подавляет предупреждение о том, что сопоставление с образцом не является исчерпывающим, второе @unchecked подавляет предупреждение о непроверенном универсальном String из-за стирания типа).

В качестве альтернативы вы можете соответствовать более безопасному типу. Но обычно писать, что аргумент является просто HList, а не конкретным A :: B :: ... :: HNil, слишком грубо. Так как ваша функция действует по-разному на значения разных типов (а именно, HNil и H :: T), это Poly.

object iter extends Poly2 {
  implicit val nilCase: Case.Aux[HNil, List[Boolean], List[Boolean]] = 
    at((_, acc) => acc)

  implicit def consCase[A, T <: HList](implicit 
    tailCase: Case.Aux[T, List[Boolean], List[Boolean]]
    ): Case.Aux[(String => A) :: T, List[Boolean], List[Boolean]] =
    at { case (head :: tail, acc) => myFunc(head) :: iter(tail, acc) }
}

Использование:

def myFunc[T](x: String => T): Boolean = true

iter(((s: String) => s.toUpperCase) :: ((s: String) => s.length) :: HNil, List[Boolean]()) 
// List(true, true)
...