2017-10-06 54 views
3

我知道用無形的,我可以做這樣的事情:一般地變身一類到另一個使用無形+ LabelledGenerics

import shapeless._, syntax.singleton._, record._ 

    case class Foo(x: Int, y: String) 
    case class RichFoo(x: Int, y: String, z: Double) 

    def makeRich(foo: Foo): RichFoo = { 
    val x = ('z ->> 0.9) 
    val repr = LabelledGeneric[Foo].to(foo) + x 
    LabelledGeneric[RichFoo].from(repr) 
    } 

    val a = Foo(1, "hello") 
    val b = makeRich(a) 

現在我想編寫一個通用的方法來做到這一點:

trait Morph[A, B, AR, BR] { 
    def apply(a: A)(f: AR => BR): B 
} 

object Morph { 
    implicit def genericMorph[A, B, AR, BR](implicit genA: LabelledGeneric.Aux[A, AR], genB: LabelledGeneric.Aux[B, BR]): Morph[A, B, AR, BR] = 
    new Morph[A, B, AR, BR] { 
     override def apply(a: A)(f: AR => BR) = genB.from(f(genA.to(a))) 
    } 

    implicit class Syntax[A](a: A) { 
    def morph[AR, BR, B](f: AR => BR)(implicit morph: Morph[A, B, AR, BR]): B = 
     morph(a)(f) 
    } 
} 

但是,現在這個用法是不可靠的?

val a = Foo(1, "hello") 
    a.morph[???, ???, RichFoo](_ + ('z ->> 0.9)) 

什麼是設計此API的更好方法?

我想是這樣的:

implicit class Syntax[A](a: A) { 
    def morphTo[B] = new { 
     def using[AR <: HList, BR <: HList](f: AR => BR)(implicit morph: Morph[A, B, AR, BR]): B = 
     morph(a)(f) 
    } 
    } 

a.morphTo[RichFoo].using(_ :+ ('z ->> 0.9)) 

但它並沒有真正的工作

回答

4

有跡象表明,防止你的榜樣工作,你所希望的方式類型推斷(兩者無關兩項限制與形狀btw):

  1. 在當前scalac明確指定類型參數是或不是。但是您只想指定B,其餘部分將被推斷出來。 Currying是解決這個問題的方法之一。所以你的嘗試是在正確的軌道上,但沒有考慮到2.

  2. 方法參數的類型推斷從左到右一次從一個參數列表流出。但是您想根據morph的類型推斷出f的類型,因爲它是隱含的,所以最後到達。這裏的解決方案是... 再次捲曲

因此,從1和2可以得出你必須咖喱兩次

implicit class Syntax[A](a: A) { 
    def morphTo[B] = new { 
    def by[AR <: HList, BR <: HList](implicit morph: Morph[A, B, AR, BR]) = new { 
     def using(f: AR => BR): B = morph(a)(f) 
    } 
    } 
} 

a.morphTo[RichFoo].by.using(_ :+ ('z ->> 0.9)) 

有一個替代解決方案爲1 - 使用僞參數指定類型參數B

trait To[-A] 
object To { 
    private val instance = new To[Any] { } 
    def apply[A]: To[A] = instance 
} 

implicit class Syntax[A](a: A) { 
    def morph[B, AR <: HList, BR <: HList](to: To[B])(
    implicit morph: Morph[A, B, AR, BR] 
) = new { 
    def using(f: AR => BR): B = morph(a)(f) 
    } 
} 

a morph To[RichFoo] using (_ :+ ('z ->> 0.9)) 

對於對這些問題是如何在斑點狗解決備查

  1. 您已經可以部分地指定類型參數:a.morph[B = RichFoo]
  2. 上有比較一致的隱含參數語法正在進行的工作:lampepfl/dotty#1260

編輯:通常,定義依賴於其他類型爲類型成員的類型是個好主意:

trait Morph[A, B] { 
    type AR 
    type BR 
    def apply(a: A)(f: AR => BR): B 
} 

object Morph { 
    type Aux[A, B, AR0, BR0] = Morph[A, B] { 
    type AR = AR0 
    type BR = BR0 
    } 

    implicit def genericMorph[A, B, AR0, BR0](
    implicit genA: LabelledGeneric.Aux[A, AR0], genB: LabelledGeneric.Aux[B, BR0] 
): Aux[A, B, AR0, BR0] = new Morph[A, B] { 
    type AR = AR0 
    type BR = BR0 
    def apply(a: A)(f: AR => BR) = genB.from(f(genA.to(a))) 
    } 

    implicit class Syntax[A](a: A) { 
    def morphTo[B](implicit morph: Morph[A, B]) = new { 
     def using(f: morph.AR => morph.BR) = morph(a)(f) 
    } 
    } 
} 
1
import shapeless._, syntax.singleton._, record._ 

case class Foo(x: Int, y: String) 
case class RichFoo(x: Int, y: String, z: Double) 

class Morph[A, B, AR](a: A, genA: LabelledGeneric.Aux[A, AR]) { 
    def apply[BR](f: AR => BR)(implicit genB: LabelledGeneric.Aux[B, BR]) = genB.from(f(genA.to(a))) 
} 

implicit class Syntax[A, AR](val a: A)(implicit genA: LabelledGeneric.Aux[A, AR]) { 
    def morph[B]: Morph[A, B, AR] = new Morph(a, genA) 
} 

val a = Foo(1, "hello") 
a.morph[RichFoo](_ + ('z ->> 0.9)) // => RichFoo(1,hello,0.9) 
+0

哈不錯的把戲減少一個隱式搜索! – pathikrit

0

根據@g。krastev的回答我這個DSL的方式去:

import shapeless._, syntax.singleton._, record._, ops.hlist._ 

case class Morph[A, AR](a: A)(implicit reprA: LabelledGeneric.Aux[A, AR]) { 
    def to[B] = new { 
    def apply[BR](f: AR => BR)(implicit reprB: LabelledGeneric.Aux[B, BR]): B = 
     reprB.from(f(reprA.to(a))) 
    } 
} 

然後我們可以使用它像:

val a = Foo(1, "hello") 
val b = Morph(a).to[RichFoo](_ + ('z ->> 0.9)) // => RichFoo(1,hello,0.9) 

我們也可以把它處理re-orderings of fields這樣的:

case class Morph[A, AR](a: A)(implicit reprA: LabelledGeneric.Aux[A, AR]) { 
    def to[B] = new {  
    def apply[BR <: HList, BR2 <: HList](f: AR => BR2)(implicit reprB: LabelledGeneric.Aux[B, BR], align: Align[BR2, BR]): B = 
     reprB.from(align(f(reprA.to(a)))) 
    } 
} 
相關問題