Использование бесформенных тегов с LabelledGenerics

Предположим, что я хотел бы пройти общее представление класса case, как описано здесь

Я определил некоторый класс типов для описания полей:

trait Described[X] extends (X => String)
object Described{
  def apply[X](x: X)(implicit desc: Described[X]) = desc(x)
}

Определен какой-то экземпляр:

implicit object DoubleDescribed extends Described[Double]{
  def apply(x: Double) =  x.formatted("%01.3f")
}

И обычный пользователь:

import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.LeftFolder

object DescrFolder extends Poly2{
  implicit def field[X, S <: Symbol](implicit desc: Described[X],
                                              witness: Witness.Aux[S]):
  Case.Aux[Seq[String], FieldType[S, X], Seq[String]] =
  at[Seq[String], FieldType[S, X]](
    (descrs, value) => descrs :+ f"${witness.value.name}: ${desc(value)}")
}

def describe[T <: Product, Repr <: HList](struct: T)
      (implicit lgen: LabelledGeneric.Aux[T,Repr],
                folder: LeftFolder.Aux[Repr, Seq[String], DescrFolder.type, Seq[String]]
                             ): String = {
  val repr = lgen.to(struct)
  val descrs = folder(repr,Vector())
  descrs.mkString(struct.productPrefix + "{", ",", "}")
}

Так что теперь я мог бы написать

case class Point(x: Double, y: Double, z: Double)
describe(Point(1,2,3.0))

и получить

res1: String = Точка {x: 1000, y: 2000,z: 3000}

Теперь я хотел бы определить некоторые метаданные поля, используя shapeless теги:

import tag._
trait Invisible
val invisible = tag[Invisible]
implicit def invisibleDescribed[X](implicit desc: Described[X])
             : Described[X @@ Invisible] =
  new Described[X @@ Invisible]{
    def apply(x: X @@ Invisible) = desc(x: X) + "[invisible]"
  }

так Described(invisible(0.5)) теперь успешно производит

res2: строка = 0,500[невидимый]

Но с переопределением

case class Point(x: Double, y: Double, z: Double @@ Invisible)

describe(Point(1,2,invisible(3.0)))

выдает ошибку компиляции:

Ошибка: расходящееся неявное расширение для типа LeftFolder.Aux[this.Out,Seq[String],DescrFolder.type,Seq[String]] начиная с метода invisibleDescribed в классе...

Я предполагаю, что тип X with Tag[Y] with KeyTag[K,X] не идентифицируется как FieldType[S, X] но не мог догадаться, как это исправить.

Как можно определить правильное LeftFolder для такой ситуации?

1 ответ

Ваша проблема не связана shapeless совсем. Это может быть фактически упрощено как:

trait Described[T]
trait Invisible

implicit val doubleDescribed: Described[Double] = ???

implicit def invisibleDescribed[T](
  implicit desc: Described[T]
): Described[T with Invisible] = ???

implicitly[Described[Double with Invisible]]

Double @@ Invisible может быть "представлен" как Double with Invisible, Обратите внимание, что Double with Invisible <: Double,

Когда компилятор пытается получить неявное Described[Double with Invisible] это правильно жалуется на расходящееся неявное расширение: doubleDescribed а также invisibleDescribed,

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

implicit def invisibleDescribed[X, I <: X @@ Invisible](
  implicit desc: Described[X]
): Described[I] = new Described[I]{
  def apply(x: I) = desc(x: X) + "[invisible]"
}
Другие вопросы по тегам