Бесформенный плоский список HList с опцией, дающий HList

Учитывая следующее

case class A(value:Int)
case class B(value:String)

val h:Option[A] :: A :: Option[B] :: Option[A] :: HNil = Some(A(1)) :: A(2) :: Some(B("two")) :: (None:Option[B]) :: HNil

Как я могу получить следующее?

A(1) :: A(2) :: B("two") :: HNil

Моя попытка ниже

trait a extends Poly1 {
    implicit def any[T] = at[T](_ :: HNil)
}
object f extends a {            
    implicit def some[T] = at[Option[T]](t => if (t.isDefined) t.get :: HNil else HNil)         
}

работает для карты

h map f

> A(1) :: HNil :: A(2) :: HNil :: B(two) :: HNil :: HNil :: HNil

но не удалось для flatMap

h flatMap f

> could not find implicit value for parameter mapper: shapeless.ops.hlist.FlatMapper[f.type,shapeless.::[Option[A],shapeless.::[A,shapeless.::[Option[B],shapeless.::[Option[B],shapeless.HNil]]]]]

1 ответ

Решение

Скорее всего, единственное, что вы можете сделать, это определить отдельные случаи для Some и для None:

trait a extends Poly1 {
  implicit def default[T] = at[T](_ :: HNil)
}
object f extends a {
  implicit def caseSome[T] = at[Some[T]](_.get :: HNil)
  implicit def caseNone = at[None.type](_ => HNil)
}

Это также означает, что вы не можете использовать общий Optionв типах, во время компиляции должно быть известно, является ли каждый элемент Some или же None:

scala> (Some(A(1)) :: A(2) :: Some(B("two")) :: None :: HNil) flatMap f
res1: shapeless.::[A,shapeless.::[A,shapeless.::[B,shapeless.HNil]]] = A(1) :: A(2) :: B(two) :: HNil

Это различие определяет тип полученного выражения: Some(1) :: HNil flatMap f будет иметь тип ::[Int, HNil], но None :: HNil flatMap f будет иметь тип просто HNil,

Этот тип информации о типе не может быть получен во время компиляции из простого Options: должен (x: Option[T]) :: HNil flatMap f иметь тип ::[T, HNil] или же HNil? Мы не знаем, пока не запустим программу и не посмотрим, какова ценность x является.

Я не уверен, есть ли какой-нибудь умный способ сделать это в любом случае и получить непрозрачный HList, но в этот момент вы откажетесь от точной информации о типе каждого элемента и длины списка, а также можете привести ее к нормальной List (и, возможно, использовать cast из бесформенного позже, если вы знаете, какой именно тип будет иметь конечный результат)

Другие вопросы по тегам