使用LabelledGenerics的无形标签

Odo*_*ois 8 scala hlist shapeless labelled-generic

假设我想穿越案例类通用表示描述这里

我已经定义了一些类型类来描述字段:

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

定义了一些实例:

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

和一般用户:

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 + "{", ",", "}")
}
Run Code Online (Sandbox Code Playgroud)

所以现在我可以写

case class Point(x: Double, y: Double, z: Double)
describe(Point(1,2,3.0))
Run Code Online (Sandbox Code Playgroud)

得到

res1:String = Point {x:1,000,y:2,000,z:3,000}

现在我想使用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]"
  }
Run Code Online (Sandbox Code Playgroud)

所以Described(invisible(0.5))现在成功地产生了

res2:String = 0,500 [隐身]

但重新定义

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

describe(Point(1,2,invisible(3.0)))
Run Code Online (Sandbox Code Playgroud)

产生编译错误:

错误:LeftFolder.Aux[this.Out,Seq[String],DescrFolder.type,Seq[String]]invisibleDescribed类中的方法开始的类型的隐式扩展分歧 ...

我认为类型X with Tag[Y] with KeyTag[K,X]不是识别,FieldType[S, X]但无法猜测如何解决它.

如何定义适合LeftFolder这种情况?

Fed*_*tta 0

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]]
Run Code Online (Sandbox Code Playgroud)

Double @@ Invisible可以“表示”为Double with Invisible。注意Double with Invisible <: Double

当编译器尝试获取隐式时,Described[Double with Invisible]它会正确地抱怨隐式扩展发散:doubleDescribedinvisibleDescribed

回到原来的代码,一个简单的解决方法就是重写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]"
}
Run Code Online (Sandbox Code Playgroud)