2014-07-30 8 views
8

basa su:informe: Obiettivo generico parametrizzato per classe caso o di un campo

import shapeless._ 

case class Content(field: Int) 
lens[Content] >> 'field 

Sto cercando di fare un metodo obiettivo di creazione, qualcosa insieme:

def makeLens[T <: Product](s: Symbol) = lens[T] >> s 

Ma sembra non evidente . È possibile farlo?

In caso contrario, il risultato finale che sto cercando di realizzare è un metodo generico per l'aggiornamento mappe nidificate con contenuti caso di classe, ad esempio:

import scalaz._ 
import Scalaz._ 
import PLens._ 
import shapeless._ 
import shapeless.contrib.scalaz._ 

def nestedMapLens[R, T <: Product](outerKey: String, innerKey: Int, f: Symbol) = 
    ~((lens[T] >> f).asScalaz) compose mapVPLens(innerKey) compose mapVPLens(outerKey) 

non riesco a farlo funzionare quando parametrizzato da T e f. Ci sono altre soluzioni idiomatiche senza caldaia?

Grazie!

risposta

9

Il problema con il tuo makeLens è che vogliamo ad es. makeLens[Content]('foo) fallire in fase di compilazione e ciò non è possibile con un normale argomento Symbol. Avete bisogno di alcuni argomenti extra impliciti per monitorare il tipo di Singleton per il nome dato e di fornire la prova che è il nome di un membro della classe caso:

import shapeless._, ops.record.{ Selector, Updater }, record.FieldType 

class MakeLens[T <: Product] { 
    def apply[K, V, R <: HList](s: Witness.Aux[K])(implicit 
    gen: LabelledGeneric.Aux[T, R], 
    sel: Selector.Aux[R, K, V], 
    upd: Updater.Aux[R, FieldType[K, V], R] 
): Lens[T, V] = lens[T] >> s 
} 

def makeLens[T <: Product] = new MakeLens[T] 

E poi:

scala> case class Content(field: Int) 
defined class Content 

scala> makeLens[Content]('field) 
res0: shapeless.Lens[Content,Int] = [email protected] 

Ma makeLens[Content]('foo) non verrà compilato (che è ciò che vogliamo).

è necessario lo stesso tipo di inseguimento per il vostro nestedMapLens:

import scalaz._, Scalaz._ 
import shapeless.contrib.scalaz._ 

case class LensesFor[T <: Product]() { 
    def nestedMapLens[K, V, R <: HList](
    outerKey: String, 
    innerKey: Int, 
    s: Witness.Aux[K] 
)(implicit 
    gen: LabelledGeneric.Aux[T, R], 
    sel: Selector.Aux[R, K, V], 
    upd: Updater.Aux[R, FieldType[K, V], R] 
): PLens[Map[String, Map[Int, T]], V] = 
    (lens[T] >> s).asScalaz.partial.compose(
     PLens.mapVPLens(innerKey) 
    ).compose(
     PLens.mapVPLens(outerKey) 
    ) 
} 

Si noti che sto assumendo un build.sbt come questo:

scalaVersion := "2.11.2" 

libraryDependencies ++= Seq(
    "com.chuusai" %% "shapeless" % "2.0.0", 
    "org.typelevel" %% "shapeless-scalaz" % "0.3" 
) 

Ora definiamo una mappa esempio e alcuni obiettivi:

val myMap = Map("foo" -> Map(1 -> Content(13))) 

val myFoo1Lens = LensesFor[Content].nestedMapLens("foo", 1, 'field) 
val myBar2Lens = LensesFor[Content].nestedMapLens("bar", 2, 'field) 

E poi:

scala> myFoo1Lens.get(myMap) 
res4: Option[Int] = Some(13) 

scala> myBar2Lens.get(myMap) 
res5: Option[Int] = None 

Questo è come "senza caldaia" come si sta per ottenere. Gli elenchi di argomenti impliciti e disordinati sono intimidatori all'inizio, ma ci si abitua abbastanza rapidamente, e il loro ruolo nel riunire diversi elementi di prova sui tipi con cui si lavora diventa abbastanza intuitivo dopo un po 'di pratica.