Reputation: 11
I am trying to convert my case class into a sequence containing a lens for each field. I've created the following simplified example to highlight the problem that I am having.
The following code will give a runtime error:
import shapeless._
case class Testing(field1: String, field2: Double)
val lenses = Seq(0,1).map(i => lens[Testing] >> i)
whereas the following does not:
import shapeless._
case class Testing(field1: String, field2: Double)
val lens1 = lens[Testing] >> 0
val lens2 = lens[Testing] >> 1
val lenses = Seq(lens1, lens2)
The actual error reads "Expression i does not evaluate to a non-negative Int literal".
I feel like this error message is misleading since the code val lens3 = lens[Testing] >> 2 (i.e. accessing one field too many) would give the same error message.
Has anyone experienced behaviour like this in shapeless? And is there an easier way to extract element lenses for each field in my Case Class into a sequence (i.e. not like @lenses in monocle where you still need to access each lens using the field name)?
Upvotes: 0
Views: 266
Reputation: 51658
lens[Testing] >> 0
lens[Testing] >> 1
are implicitly transformed to
lens[Testing] >> Nat._0
lens[Testing] >> Nat._1
and this works but
val lenses = Seq(0,1).map(i => lens[Testing] >> i)
or val lenses = Seq(Nat._0,Nat._1).map(i => lens[Testing] >> i)
doesn't.
Seq(Nat._0,Nat._1)
has type Seq[Nat]
, so i
has type Nat
(rather than specific Nat._0
, Nat._1
) and this is too rough.
The following approach with constructing HList
of lenses (rather than Seq
) seems to work:
import shapeless.{::, Generic, HList, HNil, Lens, MkHListSelectLens}
case class Testing(field1: String, field2: Double)
trait MkLensHlist[A] {
type Out <: HList
def apply(): Out
}
object MkLensHlist {
type Aux[A, Out0 <: HList] = MkLensHlist[A] { type Out = Out0 }
def instance[L, Out0 <: HList](x: Out0): Aux[L, Out0] = new MkLensHlist[L] {
override type Out = Out0
override def apply(): Out0 = x
}
def apply[A](implicit instance: MkLensHlist[A]): instance.Out = instance()
implicit def mk[A, L <: HList, Out <: HList](implicit
gen: Generic.Aux[A, L],
apply: ApplyMkHListSelectLens.Aux[L, Out]
): Aux[A, Out] = instance(apply())
}
trait ApplyMkHListSelectLens[L <: HList] {
type Out <: HList
def apply(): Out
}
object ApplyMkHListSelectLens {
type Aux[L <: HList, Out0 <: HList] = ApplyMkHListSelectLens[L] { type Out = Out0}
def instance[L <: HList, Out0 <: HList](x: Out0): Aux[L, Out0] = new ApplyMkHListSelectLens[L] {
override type Out = Out0
override def apply(): Out0 = x
}
implicit def mk[L <: HList, Out <: HList](implicit
apply: ApplyMkHListSelectLens1.Aux[L, L, Out]
): Aux[L, Out] =
instance(apply())
}
trait ApplyMkHListSelectLens1[L <: HList, L1 <: HList] {
type Out <: HList
def apply(): Out
}
object ApplyMkHListSelectLens1 {
type Aux[L <: HList, L1 <: HList, Out0 <: HList] = ApplyMkHListSelectLens1[L, L1] { type Out = Out0}
def instance[L <: HList, L1 <: HList, Out0 <: HList](x: Out0): Aux[L, L1, Out0] = new ApplyMkHListSelectLens1[L, L1] {
override type Out = Out0
override def apply(): Out0 = x
}
implicit def mk1[L <: HList, H, T <: HList, Out <: HList](implicit
lens: MkHListSelectLens[L, H],
apply: Aux[L, T, Out]
): Aux[L, H :: T, Lens[L, H] :: Out] =
instance(lens() :: apply())
implicit def mk2[L <: HList]: Aux[L, HNil, HNil] =
instance(HNil)
}
MkLensHlist[Testing]
// shapeless.MkHListSelectLens$$anon$36$$anon$17@340f438e :: shapeless.MkHListSelectLens$$anon$36$$anon$17@30c7da1e :: HNil
Upvotes: 0