Informe:genérico lente parametrizada por el caso de la clase o en el campo
Pregunta
Basado en:
import shapeless._
case class Content(field: Int)
lens[Content] >> 'field
Estoy tratando de hacer un lente-la creación de método, algo así:
def makeLens[T <: Product](s: Symbol) = lens[T] >> s
Pero parece que no es evidente.Es posible hacer?
Si no, el resultado final estoy tratando de lograr es un método genérico para la actualización de anidado Mapas con el caso de la clase de contenido, por ejemplo:
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)
No puedo ir a trabajar cuando parametrizada por T y f.Hay otros idiomáticas repetitivo de soluciones libres?
Gracias!
Solución
El problema con su makeLens
es que queremos por ejemplo makeLens[Content]('foo)
error en tiempo de compilación, y que no es posible con una ordinaria Symbol
argumento.Necesita argumentos implícitos para seguir el singleton tipo para el nombre y para proporcionar evidencia de que es el nombre de un miembro de el caso de la clase:
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]
Y luego:
scala> case class Content(field: Int)
defined class Content
scala> makeLens[Content]('field)
res0: shapeless.Lens[Content,Int] = shapeless.Lens$$anon$6@7d7ec2b0
Pero makeLens[Content]('foo)
no compile (que es lo que queremos).
Usted necesita el mismo tipo de seguimiento para su 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)
)
}
Tenga en cuenta que estoy asumiendo un build.sbt
como este:
scalaVersion := "2.11.2"
libraryDependencies ++= Seq(
"com.chuusai" %% "shapeless" % "2.0.0",
"org.typelevel" %% "shapeless-scalaz" % "0.3"
)
Ahora vamos a definir un ejemplo de mapa y algunos lentes:
val myMap = Map("foo" -> Map(1 -> Content(13)))
val myFoo1Lens = LensesFor[Content].nestedMapLens("foo", 1, 'field)
val myBar2Lens = LensesFor[Content].nestedMapLens("bar", 2, 'field)
Y luego:
scala> myFoo1Lens.get(myMap)
res4: Option[Int] = Some(13)
scala> myBar2Lens.get(myMap)
res5: Option[Int] = None
Esto es acerca de como "repetitivo" libre de como vas a conseguir.El desordenado argumento implícito de las listas son intimidante al principio, pero te acostumbras a ellos con bastante rapidez, y su papel en la conjunción de los diferentes bits de la evidencia acerca de los tipos que estás trabajando hace bastante intuitivo después de un poco de práctica.