Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
983 views
in Technique[技术] by (71.8m points)

scala - Deriving nested shapeless lenses using only a type

I'm trying to come up with something similar to Classy Lenses to use with cats-mtl. For this, I want to be able to construct a Lens based on provided types only. I found no way to do it using operations provided in shapeless, so I'm writing a new one.

import shapeless._

class Classy[O[_, _], S, A](val get: O[S, A])

object Classy {
  def apply[O[_, _], S, A](implicit ev: Classy[O, S, A]): Classy[O, S, A] = ev

  implicit def rootLens[S]: Classy[Lens, S, S] =
    new Classy(OpticDefns.id[S])

  implicit def elementLens[S, L <: HList, A](
    implicit genLens: MkGenericLens.Aux[S, L],
    aLens: MkHListSelectLens[L, A]
  ): Classy[Lens, S, A] = new Classy(aLens() compose genLens())


  implicit def composeLens[S, A, T](
    implicit lh: Lazy[Classy[Lens, S, A]],
    rh: Classy[Lens, A, T]
  ): Classy[Lens, S, T] = new Classy(rh.get compose lh.value.get)
}

Unfortunately, the case I'm after is not compiling:

Classy[Lens, String, String] // OK
Classy[Lens, (Long, String), String] // OK
Classy.composeLens[(Int, (Long, String)), (Long, String), String] // OK, explicit call with explicit params
//Classy[Lens, (Int, (Long, String)), String] // <- doesn't compile

I tried a number of combinations with Lazy/ Strict / plain implicit, but none of these have worked.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Try the following approach with redefining operations making them work deeper:

  import shapeless.{::, DepFn1, DepFn2, Generic, HList, HNil, Lens, OpticDefns}

  trait DeepGeneric[T <: Product] {
    type Repr <: HList
    def to(t : T) : Repr
    def from(r : Repr) : T
  }

  object DeepGeneric {
    type Aux[T <: Product, Repr0 <: HList] = DeepGeneric[T] { type Repr = Repr0 }
    def instance[T <: Product, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepGeneric[T] {
      override type Repr = Repr0
      override def to(t: T): Repr = f(t)
      override def from(r: Repr): T = g(r)
    }

    implicit def deepGeneric[A <: Product, L <: HList, L1 <: HList](implicit
      generic: Generic.Aux[A, L],
      hListDeepGeneric: HListDeepGeneric.Aux[L, L1]
      ): Aux[A, L1] = instance(a => hListDeepGeneric.to(generic.to(a)), l1 => generic.from(hListDeepGeneric.from(l1)))
  }

  trait HListDeepGeneric[T <: HList] {
    type Repr <: HList
    def to(t : T) : Repr
    def from(r : Repr) : T
  }

  trait LowPriorityHListDeepGeneric {
    type Aux[T <: HList, Repr0 <: HList] = HListDeepGeneric[T] { type Repr = Repr0 }
    def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepGeneric[T] {
      override type Repr = Repr0
      override def to(t: T): Repr = f(t)
      override def from(r: Repr): T = g(r)
    }

    implicit def headNotCaseClass[H, T <: HList, T_hListDeepGen <: HList](implicit
      tailHListDeepGeneric: HListDeepGeneric.Aux[T, T_hListDeepGen]
      ): Aux[H :: T, H :: T_hListDeepGen] = instance({
        case h :: t => h :: tailHListDeepGeneric.to(t)
      }, {
        case h :: t => h :: tailHListDeepGeneric.from(t)
      })
  }

  object HListDeepGeneric extends LowPriorityHListDeepGeneric {
    implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)

    implicit def headCaseClass[H <: Product, T <: HList, H_deepGen <: HList, T_hListDeepGen <: HList](implicit
      headDeepGeneric: DeepGeneric.Aux[H, H_deepGen],
      tailHListDeepGeneric: HListDeepGeneric.Aux[T, T_hListDeepGen]
      ): Aux[H :: T, H_deepGen :: T_hListDeepGen] = instance({
        case h :: t => headDeepGeneric.to(h) :: tailHListDeepGeneric.to(t)
      }, {
        case h :: t => headDeepGeneric.from(h) :: tailHListDeepGeneric.from(t)
      })
  }

  // example
  case class A(i: Int, b: Boolean)
  case class B(s: String, d: Double)
  case class C(a: A, b: B, l: Long)
  implicitly[DeepGeneric.Aux[C, (Int :: Boolean :: HNil) :: (String :: Double :: HNil) :: Long :: HNil]]

  trait DeepSelector[L <: HList, U] extends DepFn1[L] { type Out = U }

  trait LowPriorityDeepSelector {
    def instance[L <: HList, U](f: L => U): DeepSelector[L, U] = (l: L) => f(l)

    implicit def select[H, T <: HList]: DeepSelector[H :: T, H] = instance(_.head)
  }

  object DeepSelector extends LowPriorityDeepSelector {
    implicit def deepSelect[H <: HList, T <: HList, H_deepGen <: HList, U](implicit
      deepSelector: DeepSelector[H, U]
      ): DeepSelector[H :: T, U] =
      instance(l => deepSelector(l.head))

    implicit def recurse[H, T <: HList, U](implicit deepSelector: DeepSelector[T, U]): DeepSelector[H :: T, U] =
      instance(l => deepSelector(l.tail))
  }

  trait DeepReplacer[L <: HList, U, V] extends DepFn2[L, V]

  trait LowPriorityDeepReplacer {
    type Aux[L <: HList, U, V, Out0] = DeepReplacer[L, U, V] { type Out = Out0 }
    def instance[L <: HList, U, V, Out0](f: (L, V) => Out0): Aux[L, U, V, Out0] = new DeepReplacer[L, U, V] {
      override type Out = Out0
      override def apply(l: L, v: V): Out = f(l, v)
    }

    implicit def replace[T <: HList, U, V]: Aux[U :: T, U, V, (U, V :: T)] = instance((l, v) => (l.head, v :: l.tail))
  }

  object DeepReplacer extends LowPriorityDeepReplacer {
    implicit def deepReplace[H <: HList, T <: HList, U, V, H1 <: HList](implicit
      deepReplacer: Aux[H, U, V, (U, H1)]): Aux[H :: T, U, V, (U, H1 :: T)] = instance((l, v) => {
        val (u, h1) = deepReplacer(l.head, v)
        (u, h1 :: l.tail)
      })

    implicit def recurse[H, T <: HList, U, V, OutT <: HList](implicit
      deepReplacer : Aux[T, U, V, (U, OutT)]): Aux[H :: T, U, V, (U, H :: OutT)] = instance((l, v) => {
        val (u, l1) = deepReplacer(l.tail, v)
        (u, l.head :: l1)
      })
  }

  trait MkDeepGenericLens[T] {
    type Repr
    def apply(): Lens[T, Repr]
  }

  object MkDeepGenericLens {
    type Aux[T, Repr0] = MkDeepGenericLens[T] { type Repr = Repr0 }
    def instance[T, Repr0](f: => Lens[T, Repr0]): Aux[T, Repr0] = new MkDeepGenericLens[T] {
      override type Repr = Repr0
      override def apply(): Lens[T, Repr] = f
    }

    implicit def mkDeepGenericLens[T <: Product](implicit gen: DeepGeneric[T]): Aux[T, gen.Repr] =
      instance(new Lens[T, gen.Repr] {
        def get(t: T): gen.Repr = gen.to(t)
        def set(t: T)(r: gen.Repr): T = gen.from(r)
      })
  }

  trait MkHListDeepSelectLens[L <: HList, U] {
    def apply(): Lens[L, U]
  }

  object MkHListDeepSelectLens {
    def instance[L <: HList, U](f: => Lens[L, U]): MkHListDeepSelectLens[L, U] = () => f

    implicit def mKHlistDeepSelectLens[L <: HList, U](implicit
      selector: DeepSelector[L, U], replacer: DeepReplacer.Aux[L, U, U, (U, L)]): MkHListDeepSelectLens[L, U] =
      instance(new Lens[L, U] {
        def get(l: L) = selector(l)
        def set(l: L)(u: U): L = replacer(l, u)._2
      })
  }

  class Classy[O[_, _], S, A](val get: O[S, A])

  object Classy {
    def apply[O[_, _], S, A](implicit ev: Classy[O, S, A]): Classy[O, S, A] = ev

    implicit def rootLens[S]: Classy[Lens, S, S] = new Classy(OpticDefns.id[S])

    implicit def elementLens[S, L <: HList, A](implicit 
      genLens: MkDeepGenericLens.Aux[S, L],
      aLens: MkHListDeepSelectLens[L, A]
      ): Classy[Lens, S, A] = new Classy(aLens() compose genLens())
  }

  Classy[Lens, String, String] // OK
  Classy[Lens, (Long, String), String] // OK
  Classy[Lens, (Int, (Long, String), Double), String] // OK

Motivated by example https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/deephlister.scala


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...