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.