Универсальный полевой аксессуар с питанием от Shapeless - PullRequest
0 голосов
/ 06 декабря 2018

Я пытаюсь реализовать удобный универсальный метод доступа к полям на основе LabelledGeneric.Использование должно выглядеть примерно так:

case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)

val foo: Foo = ???
val bar: Bar = ???

val uhuGenField = new GenField('uhu)
val uhuFooAccess = uhuGenField.from[Foo]
val uhuBarAccess = uhuGenField.from[Bar]

def someFunWithUhu[X](xs: Seq[X], access: uhuGenField.Access[X]) = ??? 

Я потратил некоторое время, пытаясь понять, как добиться такого поведения.В конце концов я пришел к такому подходу:

import shapeless._
import shapeless.ops.record.Selector

final class GenField[V](val fieldName: Symbol) {
  val fieldWitness = Witness(fieldName)
  type FieldNameType = fieldWitness.T

  trait Access[C] {
    def get(c: C): V
  }

  def from[C](implicit lg2hl: LGtoHL[C]): Access[C] = new Access[C] {
    override def get(c: C): V = {
      val labelledGeneric = lg2hl.labelledGeneric
      val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
      selector(labelledGeneric.to(c))
    }
  }
}

// I need something like this to enable syntax like
//   genField.from[DesiredClass]
// i.e. to "latch" Repr inside a single instance
// and to don't pass it explicitly to `from` method. 
sealed trait LGtoHL[A] {
  type Repr <: HList
  val labelledGeneric: LabelledGeneric.Aux[A, Repr]
}

object LGtoHL {
  implicit def mkLGtoHL[A, ARepr <: HList](implicit lg: LabelledGeneric.Aux[A, ARepr]): LGtoHL[A] = {
    new LGtoHL[A] {
      override type Repr = ARepr
      override val labelledGeneric: LabelledGeneric.Aux[A, Repr] = lg
    }
  }
}

С моей точки зрения, это решение должно быть в порядке, но оно все еще не работает.Сбой компиляции со следующим сообщением об ошибке:

Error:(17, 41) lg2hl.Repr is not an HList type
        val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]

Почему он жалуется lg2hl.Repr is not an HList type?Repr явно определено в LGtoHL как type Repr <: HList.Что не так с моим кодом?

Очень ценю вашу помощь.

1 Ответ

0 голосов
/ 07 декабря 2018

Почему объективов недостаточно?

import shapeless.{Lens, lens}

case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)

val foo: Foo = Foo("a", 1.0, 2)
val bar: Bar = Bar(3.0, true)

val fooUhu: Lens[Foo, Double] = lens[Foo] >> 'uhu
val barUhu: Lens[Bar, Double] = lens[Bar] >> 'uhu

fooUhu.get(foo) // 1.0
barUhu.get(bar) // 3.0

Сообщение об ошибке

lg2hl.Repr не относится к типу HList

происходит отсюда: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala#L511

u.baseType(HConsSym) теперь NoType .

Я думаю, GenField[V](val fieldName: Symbol) не будет работать, так как fieldName в Witness(fieldName) должны быть известны во время компиляции.Например,

lens[Foo] >> 'uhu

работает, а

val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu

- нет.По этой причине линзы, Witness, LabelledGeneric реализуются с помощью макросов.

...