I want to build general solution for field removing from case classes.
Using this trick I built this working code:
implicit class SemiGenericIgnoringOps[T](t: T) {
def ignoring[TRepr <: HList,
V,
TargetRepr <: HList,
H <: HList](k: Witness)
(implicit
gen: LabelledGeneric.Aux[T, TRepr],
rem: Remover.Aux[TRepr, k.T, (V, TargetRepr)],
upd: Updater.Aux[TargetRepr, FieldType[k.T, V], H],
ali: Align[H, TRepr]
): SemiGeneric.Aux[T, TargetRepr] = new SemiGeneric[T] {
type Repr = TargetRepr
def convert: TargetRepr = gen.to(t) - k
}
}
I want to replace single k: Witness with HList of Witnesss. But even adding a generic Witness type parameter cause the compilation error: can't find implicit value for Remover.
implicit class SemiGenericIgnoringOps[T](t: T) {
def ignoring[TRepr <: HList,
V,
TargetRepr <: HList,
H <: HList,
W <: Witness](w: W) // added type parameter
(implicit
gen: LabelledGeneric.Aux[T, TRepr],
rem: Remover.Aux[TRepr, w.T, (V, TargetRepr)],
upd: Updater.Aux[TargetRepr, FieldType[w.T, V], H],
ali: Align[H, TRepr]
): SemiGeneric.Aux[T, TargetRepr] = new SemiGeneric[T] {
type Repr = TargetRepr
def convert: TargetRepr = gen.to(t) - w
}
}
It seems that compilator can't derive Witness.T. Trick with Witness.Aux[R] doesn't help. How to overcome this problem?
On contrary to the answer you referred to, you don't need Updater and Align since your convert works with HList / record.
The following code works:
import shapeless.ops.record.Remover
import shapeless.{::, HList, HNil, LabelledGeneric, Witness}
trait GenericAllKeysRemover[A <: Product, K <: HList] {
type Out <: HList
def apply(a: A): Out
}
object GenericAllKeysRemover {
type Aux[A <: Product, K <: HList, Out0 <: HList] = GenericAllKeysRemover[A, K] { type Out = Out0 }
def instance[A <: Product, K <: HList, Out0 <: HList](f: A => Out0): Aux[A, K, Out0] = new GenericAllKeysRemover[A, K] {
override type Out = Out0
override def apply(a: A): Out = f(a)
}
def apply[A <: Product, K <: HList](implicit genericAllKeysRemover: GenericAllKeysRemover[A, K]): Aux[A, K, genericAllKeysRemover.Out] = genericAllKeysRemover
implicit def mkGenericAllKeysRemover[A <: Product, L <: HList, K <: HList, Out <: HList](implicit
labelledGeneric: LabelledGeneric.Aux[A, L],
allKeysRemover: AllKeysRemover.Aux[L, K, Out]): Aux[A, K, Out] =
instance(a => allKeysRemover(labelledGeneric.to(a)))
}
trait AllKeysRemover[L <: HList, K <: HList] {
type Out <: HList
def apply(l: L): Out
}
object AllKeysRemover {
type Aux[L <: HList, K <: HList, Out0 <: HList] = AllKeysRemover[L, K] { type Out = Out0 }
def instance[L <: HList, K <: HList, Out0 <: HList](f: L => Out0): Aux[L, K, Out0] = new AllKeysRemover[L, K] {
override type Out = Out0
override def apply(l: L): Out0 = f(l)
}
def apply[L <: HList, K <: HList](implicit allKeysRemover: AllKeysRemover[L, K]): Aux[L, K, allKeysRemover.Out] =
allKeysRemover
implicit def mkAllKeysRemover[L <: HList]: Aux[L, HNil, L] = instance(identity)
implicit def mkAllKeysRemover1[L <: HList, H, T <: HList, V, L_removeH <: HList, Out <: HList](implicit
remover: Remover.Aux[L, H, (V, L_removeH)],
allKeysRemover: Aux[L_removeH, T, Out]): Aux[L, H :: T, Out] =
instance(l => allKeysRemover(remover(l)._2))
}
case class MyClass(i: Int, s: String, b: Boolean)
GenericAllKeysRemover[MyClass, Witness.`'s`.T :: Witness.`'b`.T :: HNil].apply(MyClass(1, "a", true)) //1 :: HNil
Related
I am looking at getting case class definitions.
From SO I gleaned this practice as per Get field names list from case class, the answer using reflection by Dia Kharrat.
Some experimenting in which I have a case class referring to another case class, nested. Can we get the metadata expanded easily in some way?
import scala.collection.mutable.ArrayBuffer
case class MyChgClass(b: Option[String], c: Option[String], d: Option[String])
case class MyFullClass(k: Int, b: String, c: String, d: String)
case class MyEndClass(id: Int, after: MyFullClass)
def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]
returns:
z1: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b)
z2: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b, value k)
z3: List[reflect.runtime.universe.MethodSymbol] = List(value after, value id)
So:
Looking to expand the case class MyEndClass.
The option aspect appears not not been supplied. Possible?
The option aspect appears not not been supplied. Possible?
Are you looking for .name and .typeSignature?
val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]
z1.map(_.name) // List(d, c, b)
z1.map(_.typeSignature) // List(Option[String], Option[String], Option[String])
z2.map(_.name) // List(d, c, b, k)
z2.map(_.typeSignature) // List(String, String, String, Int)
z3.map(_.name) // List(after, id)
z3.map(_.typeSignature) // List(MyFullClass, Int)
If your classes are known at compile time it would make sense to use compile-time reflection i.e. macros rather than runtime reflection
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def classAccessors[T]: List[(String, String)] = macro classAccessorsImpl[T]
def classAccessorsImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val pairs = weakTypeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.map(m => (m.name.toString, m.typeSignature.toString))
q"List.apply[(String, String)](..$pairs)"
}
// in a different subproject
classAccessors[MyChgClass] // List((d,Option[String]), (c,Option[String]), (b,Option[String]))
classAccessors[MyFullClass] // List((d,String), (c,String), (b,String), (k,Int))
classAccessors[MyEndClass] // List((after,MyFullClass), (id,Int))
Even better would be to use one of libraries encapsulating those macros into some type classes. In Shapeless the type class giving access to names and types of case-class fields is LabelledGeneric
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
import shapeless.{HList, LabelledGeneric, Poly0, Poly1, Typeable, Witness}
object fieldNamesAndTypesPoly extends Poly1 {
implicit def cse[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => (witness.value.name, typeable.describe))
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def classAccessors[T] = new PartiallyApplied[T]
class PartiallyApplied[T] {
def apply[L <: HList, L1 <: HList]()(implicit
labelledGeneric: LabelledGeneric.Aux[T, L],
fillWith: FillWith[nullPoly.type, L],
mapper: Mapper.Aux[fieldNamesAndTypesPoly.type, L, L1],
toList: ToList[L1, (String, String)]
): List[(String, String)] = toList(mapper(fillWith()))
}
classAccessors[MyChgClass]() // List((b,Option[String]), (c,Option[String]), (d,Option[String]))
classAccessors[MyFullClass]() // List((k,Int), (b,String), (c,String), (d,String))
classAccessors[MyEndClass]() // List((id,Int), (after,MyFullClass))
Looking to expand the case class MyEndClass.
You can try deep versions of the type classes LabelledGeneric, Mapper etc.
import shapeless.labelled.{FieldType, field}
import shapeless.{::, DepFn0, DepFn1, HList, HNil, LabelledGeneric, Poly0, Poly1, Typeable, Witness, poly}
trait DeepLabelledGeneric[T <: Product] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
object DeepLabelledGeneric {
type Aux[T <: Product, Repr0 <: HList] = DeepLabelledGeneric[T] {type Repr = Repr0}
def instance[T <: Product, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepLabelledGeneric[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
labelledGeneric: LabelledGeneric.Aux[A, L],
hListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[L, L1]
): Aux[A, L1] = instance(a => hListDeepLabelledGeneric.to(labelledGeneric.to(a)), l1 => labelledGeneric.from(hListDeepLabelledGeneric.from(l1)))
}
trait HListDeepLabelledGeneric[T <: HList] {
type Repr <: HList
def to(t: T): Repr
def from(r: Repr): T
}
trait LowPriorityHListDeepLabelledGeneric {
type Aux[T <: HList, Repr0 <: HList] = HListDeepLabelledGeneric[T] {type Repr = Repr0}
def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepLabelledGeneric[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_hListDeepLGen <: HList](implicit
tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
): Aux[H :: T, H :: T_hListDeepLGen] = instance({
case h :: t => h :: tailHListDeepLabelledGeneric.to(t)
}, {
case h :: t => h :: tailHListDeepLabelledGeneric.from(t)
})
}
object HListDeepLabelledGeneric extends LowPriorityHListDeepLabelledGeneric {
implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)
implicit def headCaseClass[K <: Symbol, H <: Product, T <: HList, H_deepLGen <: HList, T_hListDeepLGen <: HList](implicit
headDeepLabelledGeneric: DeepLabelledGeneric.Aux[H, H_deepLGen],
tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
): Aux[FieldType[K, H] :: T, FieldType[K, H_deepLGen] :: T_hListDeepLGen] = instance({
case h :: t => field[K](headDeepLabelledGeneric.to(h)) :: tailHListDeepLabelledGeneric.to(t)
}, {
case h :: t => field[K](headDeepLabelledGeneric.from(h)) :: tailHListDeepLabelledGeneric.from(t)
})
}
trait DeepMapper[P <: Poly1, In <: HList] extends DepFn1[In] {
type Out <: HList
}
trait LowPriorityDeepMapper {
def apply[P <: Poly1, L <: HList](implicit deepMapper: DeepMapper[P, L]): Aux[P, L, deepMapper.Out] = deepMapper
type Aux[P <: Poly1, In <: HList, Out0 <: HList] = DeepMapper[P, In] {type Out = Out0}
def instance[P <: Poly1, In <: HList, Out0 <: HList](f: In => Out0): Aux[P, In, Out0] = new DeepMapper[P, In] {
override type Out = Out0
override def apply(t: In): Out = f(t)
}
implicit def headNotHList[P <: Poly1, H, T <: HList](implicit
headCase: poly.Case1[P, H],
tailDeepMapper: DeepMapper[P, T]
): Aux[P, H :: T, headCase.Result :: tailDeepMapper.Out] =
instance(l => headCase(l.head) :: tailDeepMapper(l.tail))
}
object DeepMapper extends LowPriorityDeepMapper {
implicit def hNil[P <: Poly1]: Aux[P, HNil, HNil] = instance(_ => HNil)
implicit def headHList[P <: Poly1, K <: Symbol, H <: HList, H_deepMap <: HList, T <: HList](implicit
headDeepMapper: DeepMapper.Aux[P, H, H_deepMap],
headCase: poly.Case1[P, FieldType[K, H_deepMap]], // apply poly one more time
tailDeepMapper: DeepMapper[P, T]
): Aux[P, FieldType[K, H] :: T, headCase.Result :: tailDeepMapper.Out] =
instance(l => headCase(field[K](headDeepMapper(l.head))) :: tailDeepMapper(l.tail))
}
trait DeepFillWith[P <: Poly0, L <: HList] extends DepFn0 {
type Out = L
}
trait LowPriorityDeepFillWith {
def apply[P <: Poly0, L <: HList](implicit deepFillWith: DeepFillWith[P, L]): DeepFillWith[P, L] = deepFillWith
def instance[P <: Poly0, L <: HList](f: => L): DeepFillWith[P, L] = new DeepFillWith[P, L] {
override def apply(): L = f
}
implicit def headNotHList[P <: Poly0, H, T <: HList](implicit
headCase: poly.Case0.Aux[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, H :: T] =
instance(headCase() :: tailDeepFillWith())
}
object DeepFillWith extends LowPriorityDeepFillWith {
implicit def hNil[P <: Poly0]: DeepFillWith[P, HNil] = instance(HNil)
implicit def headHList[P <: Poly0, K <: Symbol, H <: HList, T <: HList](implicit
headDeepFillWith: DeepFillWith[P, H],
tailDeepFillWith: DeepFillWith[P, T]
): DeepFillWith[P, FieldType[K, H] :: T] =
instance(field[K](headDeepFillWith()) :: tailDeepFillWith())
}
trait LowPriorityFieldNamesAndTypesPoly extends Poly1 {
implicit def notHListCase[K <: Symbol, V](implicit
witness: Witness.Aux[K],
typeable: Typeable[V]
): Case.Aux[FieldType[K, V], (String, String)] =
at(_ => (witness.value.name, typeable.describe))
}
object fieldNamesAndTypesPoly extends LowPriorityFieldNamesAndTypesPoly {
implicit def hListCase[K <: Symbol, V <: HList](implicit
witness: Witness.Aux[K],
): Case.Aux[FieldType[K, V], (String, V)] =
at(v => (witness.value.name, v)) // for DeepMapper "applying this poly one more time"
}
object nullPoly extends Poly0 {
implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}
def classAccessors[T <: Product] = new PartiallyApplied[T]
class PartiallyApplied[T <: Product] {
def apply[L <: HList]()(implicit
deepLabelledGeneric: DeepLabelledGeneric.Aux[T, L],
deepFillWith: DeepFillWith[nullPoly.type, L],
deepMapper: DeepMapper[fieldNamesAndTypesPoly.type, L],
): deepMapper.Out = deepMapper(deepFillWith())
}
classAccessors[MyChgClass]() // (b,Option[String]) :: (c,Option[String]) :: (d,Option[String]) :: HNil
classAccessors[MyFullClass]() // (k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil
classAccessors[MyEndClass]() // (id,Int) :: (after,(k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil) :: HNil
Deriving nested shapeless lenses using only a type
Weird behavior trying to convert case classes to heterogeneous lists recursively with Shapeless
https://github.com/milessabin/shapeless/blob/main/examples/src/main/scala/shapeless/examples/deephlister.scala
Converting nested case classes to nested Maps using Shapeless
Automatically convert a case class to an extensible record in shapeless?
Not sure if the correct term is "distributive property" but I remember learning this in school so here's an example of what I'm trying to do:
Given:
type MyHList = (A :+: B :+: C :+: CNil) :: (Foo :+: Bar :+: CNil) :: HNil
is there any built-in type class in Shapeless that will out this:
type Out = (A, Foo) :+: (A, Bar) :+: (B, Foo) :+: (B, Bar) :+: (C, Foo) :+: (C, Bar) :+: CNil
?
Thanks
I would call such transformation cartesian, tensor or direct product (i.e. a product of each term by each term, on contrary to inner product / scalar product / zipping). Although indeed it relates to distributive law.
I guess there is no such standard type class literally but it can be expressed via standard ones
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil, Poly1, poly}
import shapeless.ops.coproduct.{FlatMap, Mapper}
trait Cartesian[L <: HList] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
implicit def mkCartesian[C <: Coproduct, C1 <: Coproduct](implicit
flatMap: FlatMap[C, MapperPoly[C1]]
): Aux[C :: C1 :: HNil, flatMap.Out] = null
trait MapperPoly[C <: Coproduct] extends Poly1
object MapperPoly {
implicit def cse[C <: Coproduct, A](implicit
mapper: Mapper[TuplePoly[A], C]
): poly.Case1.Aux[MapperPoly[C], A, mapper.Out] = null
}
trait TuplePoly[A] extends Poly1
object TuplePoly {
implicit def cse[A, B]: poly.Case1.Aux[TuplePoly[A], B, (A, B)] = null
}
}
implicitly[Cartesian.Aux[MyHList, Out]] // compiles
The type class Cartesian is now acting on type level only. It's possible that on value level its definition would be a little trickier (with poly.Case1.Aux[P, ... for P <: MapperPoly[C], poly.Case1.Aux[P, ... for P <: TuplePoly[A] rather than poly.Case1.Aux[MapperPoly[C], ..., poly.Case1.Aux[TuplePoly[A], ... and using Unpack1, see Filter a HList using a supertype ). Update: Or maybe not :)
Also there is always an option to define a custom type class recursively rather than try to deduce everything to standard type classes.
Here is recursive type-level implementation for multiple HLists of Coproducts (not necessary two)
// transforms an hlist of coproducts into a coproduct of tuples
trait Cartesian[L <: HList] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
implicit def mkCartesian[L <: HList, C <: Coproduct](implicit
cartesian: CartesianHelper.Aux[L, C],
mapper: coproduct.Mapper[tuplerPoly.type, C]
): Aux[L, mapper.Out] = null
object tuplerPoly extends Poly1 {
implicit def cse[L <: HList](implicit
tupler: hlist.Tupler[L]
): Case.Aux[L, tupler.Out] = null
}
}
// transforms an hlist of coproducts into a coproduct of hlists
trait CartesianHelper[L <: HList] {
type Out <: Coproduct
}
trait LowPriorityHelper1 {
type Aux[L <: HList, Out0 <: Coproduct] = CartesianHelper[L] { type Out = Out0 }
// (a + (a1+...)) * (b1+...) * (c1+...) * ...
// = a * ((b1+...) * (c1+...) * ...)
// + ((a1+...) * (b1+...) * (c1+...) * ...)
implicit def recurse[H, T <: Coproduct, T1 <: HList,
C <: Coproduct, C1 <: Coproduct, C2 <: Coproduct](implicit
ev: T1 <:< (_ :: _),
cartesian: Aux[T1, C],
mapper: coproduct.Mapper.Aux[PrependPoly[H], C, C1],
cartesian1: Aux[T :: T1, C2],
extendBy: coproduct.ExtendBy[C1, C2]
): Aux[(H :+: T) :: T1, extendBy.Out] = null
trait PrependPoly[H] extends Poly1
object PrependPoly {
implicit def cse[H, L <: HList]: poly.Case1.Aux[PrependPoly[H], L, H :: L] = null
}
}
trait LowPriorityHelper extends LowPriorityHelper1 {
implicit def one[C <: Coproduct](implicit
mapper: coproduct.Mapper[prependPoly.type, C]
): Aux[C :: HNil, mapper.Out] = null
object prependPoly extends Poly1 {
implicit def cse[A]: Case.Aux[A, A :: HNil] = null
}
}
object CartesianHelper extends LowPriorityHelper {
implicit def hnil: Aux[HNil, CNil] = null
implicit def cnil[T <: HList]: Aux[CNil :: T, CNil] = null
}
type MyHList1 = (A :+: B :+: C :+: CNil) :: (Foo :+: Bar :+: CNil) :: (X :+: Y :+: CNil) :: HNil
type Out1 = (A, Foo, X) :+: (A, Foo, Y) :+: (A, Bar, X) :+: (A, Bar, Y) :+: (B, Foo, X) :+: (B, Foo, Y) :+:
(B, Bar, X) :+: (B, Bar, Y) :+: (C, Foo, X) :+: (C, Foo, Y) :+: (C, Bar, X) :+: (C, Bar, Y) :+: CNil
implicitly[Cartesian.Aux[MyHList1, Out1]] // compiles
Adding value level:
def cartesian[L <: HList](l: L)(implicit cart: Cartesian[L]): cart.Out = cart(l)
trait Cartesian[L <: HList] extends DepFn1[L] {
type Out <: Coproduct
}
object Cartesian {
type Aux[L <: HList, Out0 <: Coproduct] = Cartesian[L] { type Out = Out0 }
def instance[L <: HList, Out0 <: Coproduct](f: L => Out0): Aux[L, Out0] =
new Cartesian[L] {
override type Out = Out0
override def apply(l: L): Out0 = f(l)
}
implicit def mkCartesian[L <: HList, C <: Coproduct](implicit
cartesian: CartesianHelper.Aux[L, C],
mapper: coproduct.Mapper[tuplerPoly.type, C]
): Aux[L, mapper.Out] = instance(l => mapper(cartesian(l)))
object tuplerPoly extends Poly1 {
implicit def cse[L <: HList](implicit
tupler: hlist.Tupler[L]
): Case.Aux[L, tupler.Out] = at(tupler(_))
}
}
trait CartesianHelper[L <: HList] extends DepFn1[L] {
type Out <: Coproduct
}
trait LowPriorityHelper1 {
type Aux[L <: HList, Out0 <: Coproduct] = CartesianHelper[L] { type Out = Out0 }
def instance[L <: HList, Out0 <: Coproduct](f: L => Out0): Aux[L, Out0] =
new CartesianHelper[L] {
override type Out = Out0
override def apply(l: L): Out0 = f(l)
}
implicit def recurse[H, T <: Coproduct, T1 <: HList,
C <: Coproduct, C1 <: Coproduct, C2 <: Coproduct](implicit
ev: T1 <:< (_ :: _),
cartesian: Aux[T1, C],
prepend: Prepend.Aux[H, C, C1],
cartesian1: Aux[T :: T1, C2],
extendBy: coproduct.ExtendBy[C1, C2]
): Aux[(H :+: T) :: T1, extendBy.Out] =
instance(l => {
val t1 = l.tail
val c = cartesian(t1)
l.head.eliminate(h => {
val c1 = prepend(h, c)
extendBy.right(c1)
}, t => {
val c2 = cartesian1(t :: t1)
extendBy.left(c2)
})
})
// custom type class instead of mapping with a generic Poly
trait Prepend[H, C <: Coproduct] extends DepFn2[H, C] {
type Out <: Coproduct
}
object Prepend {
type Aux[H, C <: Coproduct, Out0 <: Coproduct] = Prepend[H, C] { type Out = Out0 }
def instance[H, C <: Coproduct, Out0 <: Coproduct](f: (H, C) => Out0): Aux[H, C, Out0] =
new Prepend[H, C] {
override type Out = Out0
override def apply(h: H, c: C): Out0 = f(h, c)
}
implicit def cnil[H]: Aux[H, CNil, CNil] = instance((_, _) => unexpected)
implicit def ccons[H, L <: HList, C <: Coproduct](implicit
prepend: Prepend[H, C]
): Aux[H, L :+: C, (H :: L) :+: prepend.Out] =
instance((h, c) =>
c.eliminate(
l => Inl(h :: l),
c => Inr(prepend(h, c))
)
)
}
}
trait LowPriorityHelper extends LowPriorityHelper1 {
implicit def one[C <: Coproduct](implicit
mapper: coproduct.Mapper[prependPoly.type, C]
): Aux[C :: HNil, mapper.Out] = instance(l => mapper(l.head))
object prependPoly extends Poly1 {
implicit def cse[A]: Case.Aux[A, A :: HNil] = at(_ :: HNil)
}
}
object CartesianHelper extends LowPriorityHelper {
implicit def hnil: Aux[HNil, CNil] = instance(_ => unexpected)
implicit def cnil[T <: HList]: Aux[CNil :: T, CNil] = instance(_ => unexpected)
}
val c: C = new C {}
val bar: Bar = new Bar {}
val myHList: MyHList = Inr(Inr(Inl(c))) :: Inr(Inl(bar)) :: HNil
val res = cartesian(myHList)
res: Out // compiles
res == Inr(Inr(Inr(Inr(Inr(Inl((c, bar))))))) // true
I replaced mapping a coproduct with PrependPoly[H] by a custom type class Prepend[H, C <: Coproduct] because generic Poly are tricky and not everything can be done with them on value level.
issue #198: Injecting values to a Poly defined outside of calling method is awkward
issue #154: Improve support for partial application of Polys
Passing an extra argument into a polymorphic function?
Pick out the Nth element of a HList of Lists and return that value as a HList of values
Dynamically parametrize Poly1 function in shapeless
shapeless-dev: How to "parameterize" poly function?
HList folding function that requires the HList
Parameterise filtering of element in of shapeless Hlist of Lists
See also:
Taking HList of Seq[_] and generating Seq[HList] with cartesian product of values
Cartesian product of heterogeneous lists (Haskell)
I have the next piece of code which I want to use for type class instances creation:
def productsLogShow[HK <: HList, T, H <: HList, K](hideFieldsWithKeys: HK)(
implicit lg: LabelledGeneric.Aux[T, H], lsh: LogShow[H], keys: Keys.Aux[H, K]
): LogShow[T] = {
LogShow.create { value =>
val record = lg.to(value)
// ...
//filter somehow record and get a record without
// keys which are in 'hideFieldsWithKeys'
// ...
}
}
So, how can I filter record and do I use the correct type for hideFieldsWithKeys parameter?
UPDATED:
Full code snippet that should work according to Dmitro's answer
object Main extends App {
trait LogShow[T] {
def show(value: T): String
}
object LogShow {
def apply[T: LogShow]: LogShow[T] = implicitly[LogShow[T]]
def create[T](f: T => String): LogShow[T] = new LogShow[T] {
override def show(value: T): String = f(value)
}
}
import LogShow.create
implicit val StringShow: LogShow[String] = create(identity)
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.LeftFolder
import shapeless.ops.record.Remover
import scala.reflect.ClassTag
def productsLogShow[HK <: HList, T, H <: HList, H1 <: HList, K <: HList](hideFieldsWithKeys: HK)
(implicit
ct: ClassTag[T],
lg: LabelledGeneric.Aux[T, H],
rem: RemoverAll.Aux[H, HK, H1],
lsh: LogShow[H1]): LogShow[T] = {
LogShow.create { value =>
val record = lg.to(value)
val clearedRecord = rem(record, hideFieldsWithKeys)
s"${ct.runtimeClass.getSimpleName}:\n${lsh.show(clearedRecord)}"
}
}
trait RemoverAll[L <: HList, K <: HList] extends DepFn2[L, K]
object RemoverAll {
type Aux[L <: HList, K <: HList, Out0 <: HList] = RemoverAll[L, K] {type Out = Out0}
def create[L <: HList, K <: HList, Out0 <: HList](f: (L, K) => Out0): Aux[L, K, Out0] = new RemoverAll[L, K] {
override type Out = Out0
override def apply(l: L, k: K): Out0 = f(l, k)
}
implicit def mk[K <: HList, L <: HList, Out <: HList](implicit leftFolder: LeftFolder.Aux[K, L, removeKeys.type, Out]): Aux[L, K, Out] =
create((l, k) => leftFolder(k, l))
object removeKeys extends Poly2 {
implicit def cse[K, L <: HList, V, Out <: HList](implicit remover: Remover.Aux[L, K, (V, Out)]): Case.Aux[L, K, Out] =
at((l, _) => remover(l)._2)
}
}
implicit def hconsLogShow[K <: Symbol, H, T <: HList](implicit
wt: Witness.Aux[K],
lshHead: LogShow[H],
lshTail: LogShow[T]): LogShow[FieldType[K, H] :: T] =
LogShow.create { value =>
s"${wt.value.name}: ${lshHead.show(value.head)}\n${lshTail.show(value.tail)}"
}
implicit val hnilLogShow: LogShow[HNil] = LogShow.create(_ => "")
//test
case class Address(country: String, street: String)
implicit val inst: LogShow[Address] = productsLogShow('country :: HNil)
//could not find implicit value for parameter lg: shapeless.LabelledGeneric.Aux[T,H]
//[error] implicit val inst: LogShow[Address] = productsLogShow('country :: HNil)
println(implicitly[LogShow[Address]].show(Address("Ukraine", "Gorkogo")))
}
Try
import shapeless.ops.hlist.LeftFolder
import shapeless.ops.record.{Keys, Remover}
import shapeless.{::, DepFn2, HList, HNil, LabelledGeneric, Poly2, Witness}
import shapeless.record.Record
def productsLogShow[HK <: HList, T, H <: HList, H1 <: HList, K <: HList](hideFieldsWithKeys: HK)(
implicit lg: LabelledGeneric.Aux[T, H], lsh: LogShow[H], /*keys: Keys.Aux[H, K]*/ rem: RemoverAll.Aux[H, HK, H1]
): LogShow[T] = {
LogShow.create { value =>
val record = lg.to(value)
val record1 = rem(record, hideFieldsWithKeys)
???
}
}
trait RemoverAll[L <: HList, K <: HList] extends DepFn2[L, K]
object RemoverAll {
type Aux[L <: HList, K <: HList, Out0 <: HList] = RemoverAll[L, K] { type Out = Out0 }
def create[L <: HList, K <: HList, Out0 <: HList](f: (L, K) => Out0): Aux[L, K, Out0] = new RemoverAll[L, K] {
override type Out = Out0
override def apply(l: L, k: K): Out0 = f(l, k)
}
implicit def mk[K <: HList, L <: HList, Out <: HList](implicit leftFolder: LeftFolder.Aux[K, L, removeKeys.type, Out]): Aux[L, K, Out] =
create((l, k) => leftFolder(k, l))
object removeKeys extends Poly2 {
implicit def cse[K, L <: HList, V, Out <: HList](implicit remover: Remover.Aux[L, K, (V, Out)]): Case.Aux[L, K, Out] =
at((l, _) => remover(l)._2)
}
}
implicitly[RemoverAll.Aux[Record.`'i -> Int, 's -> String, 'b -> Boolean`.T, Witness.`'s`.T :: Witness.`'i`.T :: HNil, Record.`'b -> Boolean`.T]]
Replace the line
implicit val inst: LogShow[Address] = productsLogShow('country :: HNil)
with
import shapeless.syntax.singleton._
implicit val inst: LogShow[Address] = productsLogShow('country.narrow :: HNil)
'country had type Symbol instead of necessary singleton type Witness.`'country`.T.
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)?
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
In the example below, I get could not find implicit value for parameter zipper: shapeless.ops.hlist.Zip[shapeless.::[keys.Out,shapeless.::[mapper.Out,shapeless.HNil]]]
I presume I need another implicit, but can't work out the syntax. Ideas?
def mapCCLV[P <: Product, K <: HList, L <: HList, M <: HList](p: P)(poly: Poly1)(
implicit gen: Generic.Aux[P, L], lab: LabelledGeneric.Aux[P, K],
keys: Keys[K], mapper: Mapper.Aux[poly.type, L, M]) = {
val k = keys.apply
val v = gen.to(p).map(poly)
k.zip(v)
}
Like the error message says, you need an extra implicit parameter for a Zip type class instance, to be able to zip.
import shapeless._, shapeless.ops.hlist._, record._, ops.record._, labelled._
def mapCCLV[P <: Product, LG <: HList, K <: HList, G <: HList, M <: HList](
p: P)(poly: Poly1
)(implicit
gen: Generic.Aux[P, G],
lab: LabelledGeneric.Aux[P, LG],
keys: Keys.Aux[LG, K],
mapper: Mapper.Aux[poly.type, G, M],
zip: Zip[K :: M :: HNil]
): zip.Out = {
val k = keys()
val v = gen.to(p).map(poly)
k.zip(v)
}
More likely you want to use ZipWithKeys instead of Zip.
def mapCCLV[P <: Product, LG <: HList, K <: HList, G <: HList, M <: HList, Out <: HList](
p: P)(poly: Poly1
)(implicit
gen: Generic.Aux[P, G],
lab: LabelledGeneric.Aux[P, LG],
keys: Keys.Aux[LG, K],
mapper: Mapper.Aux[poly.type, G, M],
zwk: ZipWithKeys.Aux[K, M, Out]
) = gen.to(p).map(poly).zipWithKeys(keys())