2017-10-12 12 views
0

私は型崩れの副生成物を理解し、次の例を持ってしようとしていますが、それは動作しません:は、パラメータENCの暗黙的な値を見つけることができませんでした:CsvEncoder [形状]

import shapeless.{HList, ::, HNil} 
import shapeless.Generic 
import shapeless.{Coproduct, :+:, CNil, Inl, Inr} 

trait CsvEncoder[A] { 
    def encode(value: A): List[String] 
} 


sealed trait Shape 

final case class Rectangle(width: Double, height: Double) extends Shape 

final case class Circle(radius: Double) extends Shape 

object CsvEncoder { 

    def createEncoder[A](func: A => List[String]): CsvEncoder[A] = { 
    new CsvEncoder[A] { 
     override def encode(value: A): List[String] = func(value) 
    } 
    } 

    implicit val booleanEncoder: CsvEncoder[Boolean] = 
    createEncoder(value => if (value) List("yes") else List("no")) 

    implicit val intEncoder: CsvEncoder[Int] = 
    createEncoder(value => List(value.toString)) 

    implicit val stringEncoder: CsvEncoder[String] = 
    createEncoder(value => List(value)) 

    implicit val doubleEncoder: CsvEncoder[Double] = 
    createEncoder(value => List(value.toString)) 


    def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] = enc 

    implicit val cnilEncoder: CsvEncoder[CNil] = 
    createEncoder(cnil => throw new Exception("Inconceivable!")) 

    implicit def coproductEncoder[H, T <: Coproduct](
                implicit 
                hEncoder: CsvEncoder[H], 
                tEncoder: CsvEncoder[T] 
               ): CsvEncoder[H :+: T] = createEncoder { 
    case Inl(h) => hEncoder.encode(h) 
    case Inr(t) => tEncoder.encode(t) 
    } 

    def writeCsv[A](values: List[A])(implicit enc: CsvEncoder[A]): String = 
    values.map(value => enc.encode(value).mkString(",")).mkString("\n") 


} 

object Main { 


    def main(args: Array[String]) { 

    println("----------------------------------------------------------") 
    val shapes: List[Shape] = List(
     Rectangle(3.0, 4.0), 
     Circle(1.0) 
    ) 
    println(CsvEncoder.writeCsv(shapes)) 

    } 

} 

コンパイラは文句:

Error:(162, 32) could not find implicit value for parameter enc: CsvEncoder[Shape] 
    println(CsvEncoder.writeCsv(shapes)) 
Error:(162, 32) not enough arguments for method writeCsv: (implicit enc: CsvEncoder[Shape])String. 
Unspecified value parameter enc. 
    println(CsvEncoder.writeCsv(shapes)) 

ShapeCsvEncoderのインスタンスを作成していますか?シェイプレスは私のためにそれを気にする必要がありますか?

答えて

3

私は再読/ The Type Astronaut's Guide to Shapeless Bookを読むことをお勧めします、それは本当に素晴らしい本です。あなたが投稿したコードは、そこからのものです。


CSVエンコーダの例では、Employeeを使用し、手でCsvEncoderを書き込む(23ページ):

implicit val iceCreamEncoder: CsvEncoder[IceCream] = 
    new CsvEncoder[IceCream] { 
    def encode(i: IceCream): List[String] = 
     List(
     i.name, 
     i.numCherries.toString, 
     if(i.inCone) "yes" else "no" 
    ) 
    } 

あなたは「は手であなたをこのすべてを記述する必要がしたくないない場合もう少し定型文が必要になります。これは、本書の3.2.1で説明されて


、27ページの周り:

import shapeless.{HList, ::, HNil} 
implicit val hnilEncoder: CsvEncoder[HNil] = 
    createEncoder(hnil => Nil) 
implicit def hlistEncoder[H, T <: HList](
    implicit 
    hEncoder: CsvEncoder[H], 
    tEncoder: CsvEncoder[T] 
): CsvEncoder[H :: T] = 
    createEncoder { 
    case h :: t => 
     hEncoder.encode(h) ++ tEncoder.encode(t) 
    } 

私たちは、頭と尾を取る再帰ケース、hlistEncoderを(CsvEncoder[H :: T]を返す)、持っていますベースケースエンコーダhnilEncoder(これはCsvEncoder[HNil]を返します。なぜなら、HListは常に他のリンクリストと同様に末尾にHNilを持つからです)。

ただし、これはすべてのHListをCSVエンコードするのには十分ですが、RectangleHListではありません!前後の変換にはGenericを使用する必要があります。これは、右の前の部分の後に(私は型崩れの最も重要なツールの一つである、Generic.Auxを使用するようにコードを変換)、ブックの3.2.2で説明されています。

implicit def genericEncoder[A, R](
    implicit 
    gen: Generic.Aux[A, R], 
    enc: CsvEncoder[R] 
): CsvEncoder[A] = 
    createEncoder(a => enc.encode(gen.to(a))) 

gen.toa(発言権を変換し、 Rectangle)をHList(so、Double :: Double :: HNil、それをエンコードします。 )ShapeRectangle :+: Circle :+: CNilにも変換するので、Mainはそのまま動作します。

関連する問題