2017-11-26 6 views
3

this에서 implicits를 사용하여 스칼라에서 유추를 시뮬레이트하는 방법을 살펴 봅니다.스칼라의 typeclass 패턴에서 암시 적 정의의 런타임 비용

잘못 입력하지 않은 경우 재귀 인스턴스 선언에 대한 typecass 메서드 (Printer a => Printer (Option a))를 사용할 때마다 런타임에 새 인스턴스가 생성됩니다. 다음은 코드입니다. print을 사용할 때마다 Printer 인스턴스가 런타임에 생성됩니다. 특정 유형 (이 경우 Option[Int])에 대해 생성 된 인스턴스를 재사용 할 수 있습니까?

trait Printer[T] { 
    def print(t: T): String 
} 

object Printer { 
    implicit val intPrinter: Printer[Int] = new Printer[Int] { 
    def print(i: Int) = s"$i: Int" 
    } 

    implicit def optionPrinter[V](implicit pv: Printer[V]): Printer[Option[V]] = 
    new Printer[Option[V]] { 
     println("New Option Printer") 
     def print(ov: Option[V]) = ov match { 
     case None => "None" 
     case Some(v) => s"Option[${pv.print(v)}]" 
     } 
    } 

} 

object Main { 
    def print[T](t: T)(implicit p: Printer[T]) = p.print(t) 

    def main(args: Array[String]): Unit = { 
    val res3 = print(Option(1)) 
    val res4 = print(Option(2)) 
    println(s"res3: ${res3}") 
    println(s"res4: ${res4}") 
    } 
} 

// New Option Printer 
// New Option Printer 
// res3: Option[1: Int] 
// res4: Option[2: Int] 

답변

3

당신 말이 맞습니다. 귀하의 예에서는 모든 호출과 함께 새로운 인스턴스가 생성됩니다. 그러나이를 처리 할 수있는 몇 가지 방법이 있습니다. 내 경험상 다음과 같은 경험 법칙으로 끝납니다 :

우선, 미리 최적화하지 마십시오. 추가 인스턴스가 실제로 문제가되는지 확인하십시오. 성능과 관련이 있다고 확신하는 경우 val을 많이 코딩하면됩니다.

object Printer { 
    implicit val intPrinter: Printer[Int] = new Printer[Int] { 
    def print(i: Int) = s"$i: Int" 
    } 

    // make sure this is not visible, as you do not want to have productive code create new instances on demand 
    private[this] def optionPrinter[V](implicit pv: Printer[V]): Printer[Option[V]] = 
    new Printer[Option[V]] { 
     println("New Option Printer") 
     def print(ov: Option[V]) = ov match { 
     case None => "None" 
     case Some(v) => s"Option[${pv.print(v)}]" 
     } 
    } 

    implicit val intOptPrinter: Printer[Option[Int]] = optionPrinter[Int] 
} 

나는 더 진보 된 솔루션 shapeless를 사용 가능하다 생각합니다. 그러나, imho, 타입 클래스와 타입 레벨 프로그래밍에 대한 깊은 이해가 필요합니다.

관련 문제