2013-02-18 17 views
8

Mam następujący przypadek użycia, która występuje często w moim kodu:Jak przekonwertować listy [A] do listy [B] Używając niejawna konwersja

  • Kolekcja [A]
  • Pośrednia konwersja a do B

i chcę uzyskać zbiór B. mogę używać niejawnie jak następuje:

case class Items(underlying:List[B]) 
    import B._ 
    def apply(a:List[A]):Items = { 
    val listOfB= a.map {implicitly[A=>B]} 
    Items(listOfB) 
    } 

Jaki jest najbardziej elegancki sposób na zrobienie tego w Scali, być może przy pomocy Scalazu zrobić to samo?

Edytuj: celem mojego pytania jest znalezienie idiomatycznego sposobu, wspólnego podejścia bibliotek/programistów. W takim sensie rozwijanie mojego własnego rozwiązania pimp-my-library jest czymś, czego nie lubię, ponieważ inni ludzie piszący mój kod nie będą wiedzieli o istnieniu tej konwersji i nie będą jej używać, i będą przepisywali swoje własne. Opowiadam się za użyciem podejścia biblioteki do tych wspólnych funkcji i dlatego zastanawiam się, czy w Scalaz istnieje taka funkcja.

Odpowiedz

14

To bardzo proste, jeśli znasz typy. Po pierwsze niejawna konwersja z A do B:

implicit def conversion(a: A): B = //... 

to trzeba niejawna konwersja z List[S] do List[T] gdzie S i T są dowolne typy, dla których niejawna konwersja z S do T istnieje:

implicit def convList[S, T](input: List[S])(implicit c: S => T): List[T] = 
    input map c 

Powinno to następnie działa:

val listOfA: List[A] = //... 
val listOfB: List[B] = listOfA 

który został rozwiązany przez kompilator do:

val listOfB: List[B] = convList(listOfA)(conversion) 

gdzie S jest A i T jest B.

+0

Czy jest to część niektórych standardowych bibliotek? Nienawidzę wymyślania na nowo koła – Edmondo1984

+0

@ Edmondo1984: nie wiem, napisałem to od zera właśnie teraz, ale mogę też wymyślić nowe koło. BTW przeczytać ponownie, uogólniłem rozwiązanie, więc potrzebna jest tylko jedna niejawna konwersja 'convList' dla wszystkich typów konwertowalnych. –

9

Nie użyłbym niejawna konwersja tutaj, ale widok związany w klasie:

case class Foo(x: Int) 
case class Bar(y: Int) 
implicit def foo2Bar(foo: Foo) = Bar(foo.x) 
case class Items[A <% Bar](xs: List[A]) { 
    def apply(x: Int): Bar = xs(x) 
} 

Teraz można utworzyć instancję Items z listą Foo i wewnętrznie wykorzystać je, jak gdyby były Bar s.

scala> Items(List(Foo(1))) 
res8: Items[Foo] = Items(List(Foo(1))) 

scala> res8(0) 
res9: Bar = Bar(1) 

edit:

Niektóre wyjaśnienie, dlaczego ja nie użyłby niejawna konwersja:

konwersje niejawne mogą być niebezpieczne, gdy są w zakresie i przypadkowo konwertować rzeczy, że nie powinien się konwertować. Zawsze będę konwertować rzeczy jawnie lub poprzez granice widoku, ponieważ wtedy mogę je kontrolować, również niejawna konwersja może zmniejszyć rozmiar twojego kodu, ale także utrudnia zrozumienie dla innych. Używałbym tylko niejawnej konwersji dla wzorca rozszerzania mojej biblioteki.

Edit2:

Mogłeś jednak dodać metodę do rodzaju zbiórki, które wykonuje tę konwersję, jeśli taka metoda jest w zakresie:

trait Convertable[M[A], A] { 
    def convertTo[B](implicit f: A => B): M[B] 
} 

implicit def list2Convertable[A](xs: List[A]) = new Convertable[List, A] { 
    def convertTo[B](implicit f: A => B) = xs.map(f) 
} 

scala> implicit def int2String(x: Int) = x.toString 
int2String: (x: Int)String 

scala> List(1,2,3).convertTo[String] 
res0: List[String] = List(1, 2, 3) 

Zamiast kolejnego niejawna konwersja tutaj , Prawdopodobnie użyłbym zamiast tego czcionki typowej, ale myślę, że masz podstawowy pomysł.

1

Works zaczynając Scala 2.10:

implicit class ListOf[A](val list: List[A]) { 
    def of[B](implicit f: A => B): List[B] = list map f 
} 
implicit def int2String(i: Int) = i.toString 

// Usage 
List(1,2,3).of[String] 
+0

To działa tylko w scala 2.10 – mericano1

+0

Tak, już jest na zewnątrz – idonnie

+0

Wiem, właśnie wskazywałem, że byłoby korzystne, aby określić, że w odpowiedzi – mericano1

-1

W moim kodu używam bardziej ogólną wersję dostosowaną z roztworu Tomasz była powyżej, który obsługuje wszystkie Traversable instancje

/** Implicit conversion for Traversable instances where the elements are convertable */ 
implicit def convTrav[S, T, I[S] <: Traversable[S]](input: I[S])(implicit c: S => T): I[T] = 
    (input map c).asInstanceOf[I[T]] 

(to jest pracuję dla mnie, chociaż bardzo chciałbym się dowiedzieć, czy bardziej doświadczeni programiści Scali uważają, że jest to zły pomysł z jakiegokolwiek powodu, poza zwykłymi zastrzeżeniami dotyczącymi niejawnych konwersji)