2014-07-30 6 views
8

에 의해 파라미터 일반적인 렌즈를 기반으로 :볼품은 : 케이스 클래스 또는 필드

import shapeless._ 

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

나는 렌즈를 만드는 방법을 만들려고 노력하고, 뭔가 함께 : 그것은 명백하지 않은 것 같다

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

그러나 . 할 수 있습니까? 나는 T에 의해 매개 변수화 때 동작하지 않습니다

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) 

:

그렇지 않은 경우는, 내가 달성하기 위해 노력하고있어 최종 결과는 케이스 수준의 내용으로 중첩 된지도를 업데이트하기위한 일반적인 방법, 예를 들면이다 에프. 다른 관례적인 상용구없는 솔루션이 있습니까?

감사합니다.

답변

9

makeLens의 문제는 우리가 원하는 것입니다. 컴파일시에 makeLens[Content]('foo)이 실패하고 일반적인 Symbol 인수로는 불가능합니다. 다음

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] 

그리고 :

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

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

그러나 makeLens[Content]('foo) 당신은 주어진 이름의 싱글 유형을 추적하고이 경우 클래스의 멤버의 이름이 있다는 증거를 제공하기 위해 몇 가지 추가 암시 인수를 필요 (우리가 원하는) 컴파일되지 않습니다.

scalaVersion := "2.11.2" 

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

의는 예를 들어지도 및 일부 렌즈를 정의 할 수 있습니다 :이 같은 build.sbt 있으리라 믿고있어

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

참고 :

당신은 당신의 nestedMapLens에 대한 추적의 같은 종류가 필요합니다 :

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

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

그리고 :

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

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

이것은 대략 "상용구없는"것입니다. 지저분한 함축적 인 인수 목록은 처음에는 협박이지만, 익숙해지면 작업하는 유형에 대한 다양한 증거 자료를 모으는 역할이 약간의 연습 후에 상당히 직관적이됩니다.