2013-02-18 20 views
8

Ho il seguente caso d'uso che si verifica spesso nel mio codice:Come convertire un elenco [A] in un elenco [B] utilizzando una conversione implicita

  • Una collezione [A]
  • un implicito conversione a a B

e voglio ottenere un insieme di B. posso usare implicitamente come la seguente:

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

Qual è il modo più elegante per farlo in Scala, magari con l'aiuto di Scalaz di fare lo stesso?

Modifica: l'obiettivo della mia domanda è trovare un modo idiomatico, un approccio comune tra biblioteche e sviluppatori. In tal senso, sviluppare la mia soluzione pimp-my-library è qualcosa che non mi piace, perché le altre persone che scrivono il mio codice non conoscerebbero l'esistenza di questa conversione e non la userebbero, e riscriveranno le loro. Io preferisco usare un approccio di libreria per queste funzioni comuni ed è per questo che mi chiedo se in Scalaz esiste una tale caratteristica.

risposta

14

È piuttosto semplice se si conoscono i tipi. In primo luogo la conversione implicita A-B:

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

allora avete bisogno conversione implicita List[S]-List[T] dove S e T sono tipi arbitrari per i quali conversione implicita S-T esiste:

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

Questo dovrebbe quindi lavoro:

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

che si risolve dal compilatore per:

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

dove S è A e T è B.

+0

È in qualche modo parte di alcune librerie standard? Odio reinventare la ruota – Edmondo1984

+0

@ Edmondo1984: non lo so, l'ho scritto da zero solo ora, ma potrei anche reinventare la ruota. BTW ha letto di nuovo la mia risposta, ho generato la soluzione in modo da avere solo una conversione implicita 'convList' per tutti i tipi convertibili. –

9

Non vorrei usare una conversione implicita qui, ma una visione legata nella classe:

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

È ora possibile creare un'istanza di Items con una lista di Foo ed internamente usarli, come se fossero erano Bar s.

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

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

modificare:

Alcuni chiarimenti, sul perché non vorrei usare una conversione implicita:

conversioni implicite possono essere pericolosi, quando sono in portata e convertono accidentalmente cose, che essi non dovrebbe convertire. Converto sempre le cose in modo esplicito o tramite i limiti di vista, perché posso controllarlo, anche la conversione implicita può ridurre le dimensioni del codice, ma rende anche più difficile la comprensione per gli altri. Utilizzerei solo la conversione implicita per lo schema "estendi la mia biblioteca".

EDIT2:

Si potrebbe tuttavia aggiungere un metodo ai tipi di raccolta, che fa questa conversione, se tale metodo è portata:

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) 

Invece di usare un altro conversione implicita qui Probabilmente userò invece una classe tipografica, ma penso che tu abbia l'idea di base.

1

Opere partendo 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

Funziona solo in scala 2.10 – mericano1

+0

Sì, è già fuori – idonnie

+0

Lo so, stavo solo precisando che sarebbe stato utile specificare che nella risposta – mericano1

-1

Nel mio codice, sto usando una versione più generale adattato da soluzione Tomasz' oltre la quale gestisce tutte le istanze Traversable

/** 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]] 

(Questo è lavoro per me, anche se sono desideroso di sapere se qualche programmatore scalatore più esperto pensa che questa sia una cattiva idea per qualsiasi motivo, a parte i soliti avvertimenti sulle conversioni implicite)

Problemi correlati