Informe:optique générique paramétrable par classe de cas ou d'un champ
Question
Basé sur:
import shapeless._
case class Content(field: Int)
lens[Content] >> 'field
Je suis en train de faire un objectif-méthode de création, quelque chose de long:
def makeLens[T <: Product](s: Symbol) = lens[T] >> s
Mais il semble que la non-évidence.Est-il possible de le faire?
Si pas, le résultat final, je suis en train de réaliser est une méthode générique pour la mise à jour de imbriquées, des Cartes avec des cas-contenu de classe, par exemple:
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)
Je ne peux pas le faire fonctionner quand paramétrée par T et f.Existe-il d'autres idiomatiques réutilisable sans solutions?
Merci!
La solution
Le problème avec votre makeLens
c'est que nous voulons par exemple makeLens[Content]('foo)
l'échec au moment de la compilation, et ce n'est pas possible avec un simple Symbol
argument.Vous avez besoin d'un peu plus implicite arguments pour suivre le singleton de type pour le nom donné et de fournir la preuve que c'est le nom d'un membre de la classe de cas:
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]
Et puis:
scala> case class Content(field: Int)
defined class Content
scala> makeLens[Content]('field)
res0: shapeless.Lens[Content,Int] = shapeless.Lens$$anon$6@7d7ec2b0
Mais makeLens[Content]('foo)
ne compile pas (ce qui est ce que nous voulons).
Vous avez besoin du même type de suivi pour votre 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)
)
}
Notez que je suis en supposant une build.sbt
comme ceci:
scalaVersion := "2.11.2"
libraryDependencies ++= Seq(
"com.chuusai" %% "shapeless" % "2.0.0",
"org.typelevel" %% "shapeless-scalaz" % "0.3"
)
Maintenant, nous allons définir un exemple de carte et de certains objectifs:
val myMap = Map("foo" -> Map(1 -> Content(13)))
val myFoo1Lens = LensesFor[Content].nestedMapLens("foo", 1, 'field)
val myBar2Lens = LensesFor[Content].nestedMapLens("bar", 2, 'field)
Et puis:
scala> myFoo1Lens.get(myMap)
res4: Option[Int] = Some(13)
scala> myBar2Lens.get(myMap)
res5: Option[Int] = None
C'est à peu près aussi "passe-partout" sans que vous allez obtenir.Le désordre implicite des listes d'arguments sont intimidant au début, mais on s'habitue assez rapidement, et leur rôle dans la de rassembler les différents bits de données sur les types avec qui vous travaillez devient assez intuitif après un peu de pratique.