2014-07-30 24 views
8

parametreli jenerik mercek dayanarak:Şekilsiz: vaka sınıf veya alanda

import shapeless._ 

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

Bir mercek oluşturma yöntemini yapmaya çalışıyorum, bir şey boyunca: dışı bariz görünüyor

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

Ama . bunu yapmak mümkün mü? Ben T ve parametreli zaman işe alınamıyor

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) 

:

Değilse, elde etmek çalışıyorum Sonuçta vaka sınıf içeriği ile iç içe Maps güncellenmesi için genel usulüdür f. Herhangi bir idiomatik boiler-free çözümleri var mı?

Teşekkürler!

cevap

9

makeLens ile ilgili sorun şu ki; makeLens[Content]('foo) derleme zamanında başarısız olur ve bu sıradan bir Symbol bağımsız değişkeniyle mümkün değildir. Sonra

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] 

Ve:

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

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

Ama makeLens[Content]('foo) Sen verilen isim tekil türünü izlemek için ve harf sınıfının bir üyenin adı olduğunu kanıtlar sağlamak için bazı ekstra örtük argümanları ihtiyaç derlemeyeceğiz (istediğimiz şey).

scalaVersion := "2.11.2" 

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

Bunu örnek bir harita ve bazı lensler tanımlayalım: Böyle bir build.sbt farz ediyorum

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

Not:

Sen nestedMapLens için izleme aynı tür gerekir:

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

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

Ve sonra:

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

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

almak için gidiyoruz gibi bu yaklaşık "boilerplate serbest" olduğu gibi. Dağınık örtülü argüman listeleri ilk bakışta korkutucudur, ama onlara çok hızlı alışırsınız ve üzerinde çalıştığınız türler hakkında farklı kanıtları bir araya getirme rolleri küçük bir uygulamadan sonra oldukça sezgisel hale gelir.

İlgili konular