Использование бесформенных тегов с 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]"
}