Автоматически передавать HList в запись

Моя цель — автоматически переводить HList в Record по требованию.

Обратите внимание, что приведенный ниже код написан на Scala 2.13 и использует одноэлементные типы вместо Symbols.

import shapeless._
import shapeless.record._
import shapeless.labelled._

type BestBeforeDate = Record.`"day" -> Option[Int], "month" -> Option[Int], "year" -> Int`.T

object PolyToField extends Poly1 {
   implicit def mapA[A, K]: Case.Aux[A, FieldType[K, A]] = at[A](a => field[K][A](a))
}

val x: BestBeforeDate = (None :: None :: 12 :: HNil)
   .map(PolyToField)

Я получаю эту ошибку:

type mismatch;
 found   : None.type with shapeless.labelled.KeyTag[Nothing,None.type] :: None.type with shapeless.labelled.KeyTag[Nothing,None.type] :: Int with shapeless.labelled.KeyTag[Nothing,Int] :: shapeless.HNil
 required: emergencymanager.commons.data.package.BestBeforeDate
    (which expands to)  Option[Int] with shapeless.labelled.KeyTag[String("day"),Option[Int]] :: Option[Int] with shapeless.labelled.KeyTag[String("month"),Option[Int]] :: Int with shapeless.labelled.KeyTag[String("year"),Int] :: shapeless.HNil

Похоже, параметр второго типа функции Poly1 выводится как Nothing вместо того, что мне нужно.

Как я могу этого добиться?


person Markus Appel    schedule 09.04.2020    source источник


Ответы (1)


Во-первых, в атрибуте типа следует указать, что None имеет тип Option[...] (по этой причине в Cats есть none[...] и .some, см. также, пункт 9). Или используйте Option.empty[...].

Во-вторых, вы должны использовать преобразователь с возвращаемым типом вместо стандартного shapeless.ops.hlist.Mapper

trait MapperWithReturnType[HF, Out <: HList] extends Serializable {
  type In <: HList
  def apply(t: In): Out
}

object MapperWithReturnType {
  type Aux[HF, Out <: HList, In0 <: HList] = 
    MapperWithReturnType[HF, Out] { type In = In0 }
  def instance[HF, Out <: HList, In0 <: HList](f: In0 => Out): Aux[HF, Out, In0] = 
    new MapperWithReturnType[HF, Out] {
      override type In = In0
      override def apply(t: In0): Out = f(t)
    }

  implicit def hnilMapper[HF <: Poly]: Aux[HF, HNil, HNil] = instance(_ => HNil)

  implicit def hconsMapper[HF <: Poly, InH, InT <: HList, OutH, OutT <: HList](implicit
    hc : poly.Case1.Aux[HF, InH, OutH],
    mt : Aux[HF, OutT, InT]
  ): Aux[HF, OutH :: OutT, InH :: InT] = instance(l => hc(l.head) :: mt(l.tail))
}

implicit final class HListOps[L <: HList](l : L) extends Serializable {
  def mapWithReturnType[Out <: HList](f: Poly)(implicit 
    mapper: MapperWithReturnType.Aux[f.type, Out, L]
  ): Out = mapper(l)
}

val x = ((None: Option[Int]) :: (None: Option[Int]) :: 12 :: HNil)
    .mapWithReturnType[BestBeforeDate](PolyToField)

// val x = (Option.empty[Int] :: Option.empty[Int] :: 12 :: HNil)
//    .mapWithReturnType[BestBeforeDate](PolyToField)

x: BestBeforeDate
person Dmytro Mitin    schedule 09.04.2020
comment
Спасибо, это решение работает хорошо. Любая идея, почему это еще не включено в бесформенное? - person Markus Appel; 10.04.2020
comment
@MarkusAppel Существует так много разных возможных классов типов ... :) - person Dmytro Mitin; 10.04.2020