2015-08-27 24 views
8

Załóżmy chciałbym przechodzić klasy przypadek ogólny reprezentacji jak opisano hereKorzystanie bezkształtne tagi z LabelledGenerics

Mam zdefiniowane jakieś typeclass opisania pola:

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

Defined pewną instancję:

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

Ogólny użytkownik:

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 + "{", ",", "}") 
} 

Więc teraz mogę napisać

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

i dostać

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

Teraz chciałbym określić pewne pole metadanych za pomocą shapeless tagi:

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]" 
    } 

tak Described(invisible(0.5)) teraz pomyślnie produkuje

res2: String = 0,500[invisible]

Ale z przedefiniowana

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

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

daje błąd kompilacji:

Error: diverging implicit expansion for type LeftFolder.Aux[this.Out,Seq[String],DescrFolder.type,Seq[String]] starting with method invisibleDescribed in class ...

Przypuszczam, że typ X with Tag[Y] with KeyTag[K,X] nie identyfikuje jako FieldType[S, X] ale nie mógł odgadnąć, jak to naprawić.

Jak zdefiniować prawidłową dla takiej sytuacji LeftFolder?

Odpowiedz

0

Twój problem nie dotyczy w całości shapeless. To może być rzeczywiście uprościć jako:

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 może być "reprezentowane" w Double with Invisible. Zauważ, że Double with Invisible <: Double.

Gdy kompilator próbuje uzyskać niejawne Described[Double with Invisible], poprawnie narzeka na rozbieżne rozszerzenie niejawne: doubleDescribed i invisibleDescribed.

Wracając do oryginalnego kodu, łatwo naprawić może być po prostu przepisać invisibleDescribed jak:

implicit def invisibleDescribed[X, I <: X @@ Invisible](
    implicit desc: Described[X] 
): Described[I] = new Described[I]{ 
    def apply(x: I) = desc(x: X) + "[invisible]" 
}