2017-10-12 6 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로 인코더 예 (23 페이지) Employee를 사용하고, 손으로 CsvEncoder를 기록 : 손으로 모든이를 작성해야하는 하지 원하는 당신이 경우

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

를, 당신 ' 조금 더 상용구가 필요합니다. 27 페이지의 주위에


이 책의 3.2.1에 설명되어 있습니다 :

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 (HList는 항상 다른 모든 연결 목록과 마찬가지로 끝에 HNil을 가지고 있기 때문에 CsvEncoder[HNil]을 반환합니다).

그러나 이것은 HList를 CSV 인코딩하는 것으로 충분하지만 RectangleHList이 아닙니다! 앞뒤로 변환하려면 Generic을 사용해야합니다. 말

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 변환하는 (이이 책의 3.2.2에 설명되어 있습니다, 바로 이전 부분 이후 (나는 볼품의 가장 중요한 도구 중 하나 인 Generic.Aux를 사용하도록 코드를 변환) Rectangle)를 HList (so, Double :: Double :: HNil)로 인코딩하고 인코딩합니다. 또한 ShapeRectangle :+: Circle :+: CNil으로 변환하므로 Main이있는 그대로 작동합니다.