2015-12-03 16 views
6

Nowe bezkształtne i mam pytanie dotyczące używania polimorficznych funkcji, które wymagają pewnych zależności. I w zasadzie mieć ten kod i chcą wyciągnąć somePoly obiekt z metody run:Wyciąganie bezkształtnych polimorficznych funkcji, które mają zależności

import shapeless._ 
object SomeObject { 
    type SomeType = Int :+: String :+: (String, Int) :+: CNil 

    def run(someList: List[SomeType], someInt:Int, someWord:String) = { 

     object somePoly extends Poly1 { 
      implicit def doIt = at[Int](i => i + someInt + someWord.length) 
      implicit def doIt2 = at[String](i => i.length + someWord.length) 
      implicit def doIt3 = at[(String, Int)](i => i._1.length + someWord.length) 
     } 

     someList.map(_.map(somePoly)) 
    } 
} 

jeden sposób myślałem robi tak było, ale wydaje się plączą:

object TypeContainer { 
    type SomeType = Int :+: String :+: (String, Int) :+: CNil 
} 
case class SomePolyWrapper(someList: List[TypeContainer.SomeType], someInt:Int, someWord:String){ 
    object somePoly extends Poly1 { 
     implicit def doIt = at[Int](i => i + someInt + someWord.length) 
     implicit def doIt2 = at[String](i => i.length + someWord.length) 
     implicit def doIt3 = at[(String, Int)](i => i._1.length + someWord.length) 
    } 
} 
object SomeObject { 

    def run(someList: List[TypeContainer.SomeType], someInt:Int, someWord:String) = { 

     val somePolyWrapper = SomePolyWrapper(someList, someInt, someWord) 

     someList.map(_.map(somePolyWrapper.somePoly)) 
    } 
} 

ktoś ma jakieś Rada?

+0

Powiązana dyskusja na temat gittera https://gitter.im/milessabin/shapeless?at=56608190d2a5a7813cd41422 – cvogt

Odpowiedz

4

Ograniczenia domyślnego systemu rozdzielczości Scala oznaczają, że definicja Poly musi być stabilnym identyfikatorem, co sprawia, że ​​tego rodzaju rzeczy są bardziej bolesne niż powinny. Jak wspomniałem na Gitter, istnieje kilka obejść, które znam (mogą być inne).

Jedno podejście byłoby dokonać Poly1 się PolyN, gdzie dodatkowe argumenty są dla wartości someInt i someWord. Jeśli mapowałeś nad numerem HList, używałbyś wtedy mapConst i zip, aby wejście HList miało właściwy kształt. Nigdy nie robiłem tego dla produktu koprodukcyjnego, ale coś podobnego prawdopodobnie zadziała.

Innym podejściem jest użycie niestandardowej klasy typów. W twoim przypadku, może wyglądać tak:

import shapeless._ 

trait IntFolder[C <: Coproduct] { 
    def apply(i: Int, w: String)(c: C): Int 
} 

object IntFolder { 
    implicit val cnilIntFolder: IntFolder[CNil] = new IntFolder[CNil] { 
    def apply(i: Int, w: String)(c: CNil): Int = sys.error("Impossible") 
    } 

    def instance[H, T <: Coproduct](f: (H, Int, String) => Int)(implicit 
    tif: IntFolder[T] 
): IntFolder[H :+: T] = new IntFolder[H :+: T] { 
    def apply(i: Int, w: String)(c: H :+: T): Int = c match { 
     case Inl(h) => f(h, i, w) 
     case Inr(t) => tif(i, w)(t) 
    } 
    } 

    implicit def iif[T <: Coproduct: IntFolder]: IntFolder[Int :+: T] = 
    instance((h, i, w) => h + i + w.length) 

    implicit def sif[T <: Coproduct: IntFolder]: IntFolder[String :+: T] = 
    instance((h, i, w) => h.length + i + w.length) 

    implicit def pif[T <: Coproduct: IntFolder]: IntFolder[(String, Int) :+: T] = 
    instance((h, i, w) => h._1.length + i + w.length) 
} 

A potem można napisać bardziej ogólną wersję swojej run:

def run[C <: Coproduct](
    someList: List[C], 
    someInt: Int, 
    someWord: String 
)(implicit cif: IntFolder[C]): List[Int] = someList.map(cif(someInt, someWord)) 

i używać go tak:

scala> run(List(Coproduct[SomeType](1)), 10, "foo") 
res0: List[Int] = List(14) 

scala> run(List(Coproduct[SomeType](("bar", 1))), 10, "foo") 
res1: List[Int] = List(16) 

Specyfika tej operacji sprawia, że ​​podejście to wydaje się trochę dziwne, ale jeśli naprawdę potrzebowałem czegoś takiego dla różnych produktów ubocznych, prawdopodobnie to rozwiązanie, które wybrałbym.

+0

Awesome, thanks! – azuras

Powiązane problemy