I have basic type pool defined like that:
sealed trait Section
final case class Header(...) extends Section
final case class Customer(...) extends Section
final case class Supplier(...) extends Section
final case class Tech(...) extends Section
I'd like to present some case classes composed of types from this pool like this:
final case class ContractViewPartners(customer: Customer, supplier: Supplier)
final case class ContractView(header: Header, partners: ContractViewPartners, tech: Tech)
As they would be heavily used in feature-generators implemented via transfoming to HLists using method described here, I'd like to ensure that each field of presented type is one of
Section subtype
HList of Section subtypes
record presentable as HList of Section subtypes
I've defined simple compile-time checker for this condition:
object traverseView extends Poly1 {
implicit def caseSection[S <: Section] = at[S](_ => ())
implicit def caseSectionList[L <: HList]
(implicit evt: ToTraversable.Aux[L, List, Section]) = at[L](_ => ())
implicit def caseRecord[R, L <: HList]
(implicit lgen: LabelledGeneric.Aux[R, L],
trav: ToTraversable.Aux[L, List, Section]) = at[R](_ => ())
}
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
gen map traverseView
}
But it fails with (package names removed)
could not find implicit value for parameter mapper:
Mapper[traverseView.type,::[Header with KeyTag[Symbol with
Tagged[String("header")],Header],::[ContractViewPartners with
KeyTag[Symbol with
Tagged[String("partners")],ContractViewPartners],::[Tech with
KeyTag[Symbol with Tagged[String("tech")],Tech],HNil]]]]
If i remove partners section from ContractView it's working and if i try to resolve implicits on ContractViewPartners they will be found too.
Again while writing question i've found solution with adding .values like that
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
.values //!!!
gen map traverseView
}
Could it be that type with KeyTag[...] is not working properly as source for LabelledGeneric transformation?
The problem is that Case is invariant, so the fact that you have a Case instance for ContractViewPartners doesn't mean that you have a case instance for ContractViewPartners with a type-level label (which is only a subtype of ContractViewPartners). You can fix this pretty straightforwardly by generating instances for e.g. FieldType[K, ContractViewPartners] (for some arbitrary K):
sealed trait Section
final case class Header(s: String) extends Section
final case class Customer(s: String) extends Section
final case class Supplier(s: String) extends Section
final case class Tech(s: String) extends Section
final case class ContractViewPartners(customer: Customer, supplier: Supplier)
final case class ContractView(header: Header, partners: ContractViewPartners, tech: Tech)
import shapeless._, labelled.FieldType, ops.hlist.ToList
object traverseView extends Poly1 {
implicit def caseSection[S <: Section] = at[S](_ => ())
implicit def caseSectionList[K, L <: HList](implicit
lub: ToList[L, Section]
) = at[FieldType[K, L]](_ => ())
implicit def caseRecord[K, C, L <: HList](implicit
gen: Generic.Aux[C, L],
lub: ToList[L, Section]
) = at[FieldType[K, C]](_ => ())
}
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
gen map traverseView
}
You could also just use Generic[ContractView] in contractViewIsMultiSection if you don't care about the labels.
I would probably suggest not using Poly1 for this kind of thing, though. If you just want evidence that the types are right, you could do that a little more cleanly with a custom type class.
Related
I'm trying to generalize some sort of isIntanceOf based on a sealed hierarchy, but haven't been successful. The example below demonstrates what I want to achieve:
sealed abstract class Context
object Context {
case object Context1 extends Context
case class Context2(someInfo: String) extends Context
case object Context3 extends Context
}
case class ContextHolder(id: String, contexts: Set[Context])
import Context._
val holder = ContextHolder("1", Set(Context1, Context2("Other info")))
val contains1Or2 = holder.contexts.contains(Context1) || holder.contexts.exists(_.isInstanceOf[Context2])
val contains3 = holder.contexts.contains(Context3)
println(s"contains1Or2: $contains1Or2")
println(s"contains3: $contains3")
Basically I want to generalize the code of contains1Or2 and contains3 in some sort of:
def containsAnyOf[T <: Context: ClassTag](holder: ContextHolder, contexts: T*): Boolean
And then be able to use it like:
val contains1Or2 = containsAnyOf(holder, Context1, Context2)
val contains3 = containsAnyOf(holder, Context3)
I've already tried a few different approaches, but couldn't get it to work so far. Any ideas? Thanks.
The best I could come up was this:
def containsAnyOf(holder: ContextHolder)(contexts: PartialFunction[Context, Unit]): Boolean = {
val p = contexts.lift.andThen(_.isDefined)
holder.contexts.exists(p)
}
Which you can use like this:
val contains1Or2 = containsAnyOf(holder) {
case Context1 =>
case Context2(_) => // or case _: Contaxt2 =>
}
val contains3 = containsAnyOf(holder) {
case Context3 =>
}
Code running here.
Shapeless's LiftAll type class is very handy here.
import shapeless.ops.hlist.{LiftAll, ToTraversable}
import shapeless.{Generic, HList}
final class ContainsMulti[T <: Product](private val dummy: Boolean = true) extends AnyVal {
def apply[H <: HList, O <: HList](holder: ContextHolder)(implicit
toHList: Generic.Aux[T, H],
classTags: LiftAll.Aux[ClassTag, H, O],
allCtxts: LiftAll[({type E[T] = T <:< Context})#E, H],
toList: ToTraversable.Aux[O, List, ClassTag[_]]
) = classTags.instances.toList
.map(_.runtimeClass)
.forall(klazz => holder.contexts.exists(klazz.isInstance))
}
def containsMulti[T <: Product] = new ContainsMulti[T]
Usage:
val contains1Or2 = containsMulti[(Context1.type, Context2)](holder)
val contains3 = containsMulti[Tuple1[Context3.type]](holder)
val contains1and3 = containsMulti[(Context1.type, Context3.type)](holder)
val check = containsMulti[(Int, String)](holder) //doesn't compile
println(s"contains1Or2: $contains1Or2") //true
println(s"contains3: $contains3") //false
println(s"contains1and3: $contains1and3")//false
See it run.
All the implicits may seem daunting, but it's actually very simple. The extra class is so we can take multiple type parameter lists, where the first is explicitly given, and the second is inferred. Generic turns the inputted tuple into an HList, LiftAll finds the ClassTag instances for each of the types in the tuple, and ToTraversable turns the HList containing those class tags back into a List.
As Luis Miguel Mejía Suárez suggested, I have made ContainsMulti a value class, using the partially applied type trick to avoid object creation. They also pointed out that this approach initially allowed (Int, String) as input, so now allCtxts checks that all the types in the HList extend Context.
I have the following type definitions:
trait Content
trait Wrapper {
type ContentType
}
final case class Foo(param: String) extends Content
final case class Bar(param: String) extends Content
final case class FooWrapper(foo: Foo) extends Wrapper { type ContentType = Foo }
final case class BarWrapper(bar: Bar) extends Wrapper { type ContentType = Bar }
Given a content value at runtime, I would like to return it wrapped in its corresponding wrapper type. I tried the following using Shapeless:
def fetchWrapper[N, G <: Wrapper](
implicit
gen: Generic.Aux[G, N :: HNil],
// this also compiles, as an alternative to Generics.Aux
// =:= :G#ValueType =:= N
) = ...
It works, but only if I explicitly provide the type parameters: fetchWrapper[Foo, FooWrapper]. How do I take advantage of implicit resolution to generalize things so that I can derived the correct wrapper for a given content?
I was thinking of generating an instance of the wrapper using the same derivation technique in the random number generator section of the shapeless book (i.e a typelcass that produces BarWrapper if I have an implicit Bar :: HNil in scope), but I can't even find the correct wrapper type in the first place.
Generic can easily help with transforming Wrapper subtype into Content subtype but you want vice versa.
Try a type class
trait ContentToWrapper[C <: Content] {
type Out <: Wrapper { type ContentType = C }
}
object ContentToWrapper {
implicit val foo: ContentToWrapper[Foo] { type Out = FooWrapper } = null
implicit val bar: ContentToWrapper[Bar] { type Out = BarWrapper } = null
}
def fetchWrapper[C <: Content](implicit ctw: ContentToWrapper[C]): ctw.Out = ???
If you make Wrapper sealed you can derive the type class
import shapeless.{Coproduct, Generic, HList, Poly1, poly}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.CollectFirst
object ContentToWrapper {
implicit def mkContentToWrapper[C <: Content, WC <: Coproduct, WL <: HList](implicit
generic: Generic.Aux[Wrapper, WC],
toHList: ToHList.Aux[WC, WL], // there is CollectFirst for HList but not for Coproduct
collect: CollectFirst[WL, WrapperSubtypePoly[C]]
): ContentToWrapper[C] { type Out = collect.Out } = null
trait WrapperSubtypePoly[C] extends Poly1
object WrapperSubtypePoly {
implicit def cse[C, A <: Wrapper { type ContentType = C }]:
poly.Case1.Aux[WrapperSubtypePoly[C], A, A] = poly.Case1(identity)
}
}
Testing:
val w1 = fetchWrapper[Foo]
w1: FooWrapper
val w2 = fetchWrapper[Bar]
w2: BarWrapper
I'm trying to convert a case class into another via conversion to HList.
case class Source(p1:S1, p2:S2) -> HList[S1:+:S2] -> HList[D1:+:D2] ->case class Destination(p1:D1,p2:D2)
I can convert from Source to HList via gem.to and from HList to Destination via gen.from.
I wrote a Converter for each type of parameter on Source to convert it to its corresponding type in Destination but I am unsure how to recursively traverse the HList. My attempt is shown below in hlistEncoder
trait Converter[T] {
def convert(t:T): Datastructure
}
object Converter {
implicit object StrDatastructure extends Converter[String]{
def convert(t:String) = Datastructure.Str(t)
}
implicit object NumDatastructure extends Converter[Double]{
def convert(t :Double) = Datastructure.Num(t)
}
implicit object IncDatastructure extends Converter[Inc]{
def convert(t :Inc) = Datastructure.Incc(t)
}
implicit def SeqDatastructure[T: Converter]: Converter[Seq[T]] = new Converter[Seq[T]]{
def convert(t: Seq[T]) = {
Datastructure.Listt(t.map(implicitly[Converter[T]].convert):_*)
}
}
//HList traversals
implicit object hnilDatastructure extends Converter[HNil]{
def convert(t: HNil) = Datastructure.Hnill(t)
}
implicit def hlistEncoder[H, T <: HList](implicit
hEncoder: Converter[H],
tEncoder: Converter[T]
): Converter[H :: T] = new Converter[H :: T] {
def apply(h:H, t:T)= {
case (h :: t) => hEncoder.convert(h) ++ tEncoder.convert(t)
}
}
}
I use this method to test HList to HList conversion
def convertToDatastructureN[T](x: T)(implicit converter: Converter[T]): Datastructure = {
converter.convert(x)
}
case class Inc(i:Int)
case class Source(i: Int, n:Inc)
val x = Generic[Source]
val xHlist = x.to(Source(99, Inc(5)))
convertToDatastructureN(xHlist)
Any ideas how to implement hlistEncoder?
I guess you have
sealed trait Datastructure
object Datastructure {
case class Str(t: String) extends Datastructure
case class Num(t: Double) extends Datastructure
case class Incc(t: Inc) extends Datastructure
case class Listt(t: Datastructure*) extends Datastructure
case class Hnill(t: HNil) extends Datastructure
}
You want your type class Converter to transform T into Datastructure. But also (HList[S1:+:S2] -> HList[D1:+:D2], where I guess :: should be instead of :+:) you want subtypes of HList to be transformed into subtypes of HList (not into HList itself since otherwise Generic can't restore case class). So either you should modify your type class
trait Converter[T] {
type Out
def convert(t:T): Out
}
or you need two type classes: your original Converter and
trait HListConverter[T <: HList] {
type Out <: HList
def convert(t:T): Out
}
Moreover, currently your Converter is pretty rough. It transforms every T into Datastructure instead of into specific subtypes of Datastructure. This means Generic will be able to restore case classes only of shapes
MyClass(x: Datastructure)
MyClass(x: Datastructure, y: Datastructure)
...
Is it really what you want? If so then ok, if not and you need
MyClass(x: Str)
MyClass(x: Num, y: Incc)
...
then again you need
trait Converter[T] {
type Out
def convert(t:T): Out
}
Instead of HListConverter you can use standard shapeless.ops.hlist.Mapper.
My problem is that i want to mix in traits to some existing object instance
instead of doing
sealed trait Type
trait A extends Type
trait B extends Type
case class Basic(int, bool)
val someInt = 2
val someBool = False
val someType = "a"
someType match {
case "a" => new Basic(someInt, someBool) with A
case "b" => new Basic(someInt, someBool) with B
}
i would like to do new Basic() only once
and then add somehow A and B
Is it possible to do it somehow with Shapeless ?
I was hoping to be able to use Generic to get a HList, add the marker trait info in it, and convert it back to what would be a Basic with the trait attached. But I don't actually see how that would be possible with HList.
For example you can define a Poly.
import shapeless.Poly1
object myPoly extends Poly1 {
implicit val aCase: Case.Aux[A, Basic with A] = at(_ => new Basic() with A)
implicit val bCase: Case.Aux[B, Basic with B] = at(_ => new Basic() with B)
}
myPoly(new A {})
myPoly(new B {})
I guess HList is irrelevant here since Generic transforms Type into a coproduct rather than HList.
import shapeless.{:+:, CNil, Generic}
sealed trait Type
case class A() extends Type
case class B() extends Type
implicitly[Generic.Aux[Type, A :+: B :+: CNil]]
You can use Poly and singleton types:
import shapeless.{Poly1, Witness}
import shapeless.syntax.singleton._
sealed trait Type
trait A extends Type
trait B extends Type
case class Basic(int: Int, bool: Boolean)
val someInt: Int = 2
val someBool: Boolean = false
val someType: String = "a"
object myPoly extends Poly1 {
implicit val aCase: Case.Aux[Witness.`"a"`.T, Basic with A] = at(_ => new Basic(someInt, someBool) with A)
implicit val bCase: Case.Aux[Witness.`"b"`.T, Basic with B] = at(_ => new Basic(someInt, someBool) with B)
}
myPoly("a".narrow)
myPoly("b".narrow)
// myPoly("c".narrow) // doesn't compile
An example case class: Was trying to create a generic query builder
case class BaseQuery(operand: String, value: String)
case class ContactQuery(phone: BaseQuery, address: BaseQuery)
case class UserQuery(id: BaseQuery, name: BaseQuery, contact: ContactQuery)
val user = UserQuery(BaseQuery("equal","1"), BaseQuery("like","Foo"), ContactQuery(BaseQuery("eq","007-0000"),BaseQuery("like", "Foo City")))
//case class Contact(phone: String, address: String)
//case class User(id: Long, name: String, contact: Contact)
//val user = User(1, "Foo Dev", Contact("007-0000","Foo City"))
How can we retrieve the field names and respective values in scala,
On further research,
Solutions using Scala Reflection:
def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
// The above snippet returns the field names, and as input we have to
//feed the 'TypeTag' of the case class. And since we are only feeding a
//TypeTag we will not have access to any object values. Is there any Scala
// Reflection variant of the solution.
Another solution,
def getMapFromCaseClass(cc: AnyRef) =
(scala.collection.mutable.Map[String, Any]() /: cc.getClass.getDeclaredFields)
{
(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
// The above snippet returns a Map[String, Any] if we feed the case class
//object. And we will need to match the map value to any of our other case
//classes. If the class structure were simple, the above solution would be
//helpful, but in case of complex case class this would not be the most efficient solution.
Trying to:
I am actually trying to retrieve the list of field and their values during runtime.
One of the use cases,
val list: Seq[Somehow(Field+Value)] = listingFieldWithValue(obj: UserQuery)
for(o <- list){
o.field.type match{
case typeOne: FooType =>{
//Do something
}
case typeTwo: FooBarType =>{
//Do something else
}
}
}
Not really scala-reflect solution, but shapeless one. Shapeless is built on top of implicit macro, so it way faster than any reflection, if you have type information at compile time.
For implementation of your updated requirements you'll need some time to carry needed type information
sealed trait TypeInfo[T]{
//include here thing you like to handle at runtime
}
Next you can define such info for different types and provide implicit resolution. In my example such implementation are also suitable for later matching
implicit case object StringField extends TypeInfo[String]
case class NumericField[N](implicit val num: Numeric[N]) extends TypeInfo[N]
implicit def numericField[N](implicit num: Numeric[N]): TypeInfo[N] = new NumericField[N]
case class ListField[E]() extends TypeInfo[List[E]]
implicit def listField[E] = new ListField[E]
Then we define more suitable for pattern matching result structure
sealed trait FieldDef
case class PlainField[T](value: Any, info: TypeInfo[T]) extends FieldDef
case class EmbeddedType(values: Map[Symbol, FieldDef]) extends FieldDef
So any result will be either value container with needed type info or container for deeper look.
Finally we can define concept implementation
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{ToTraversable, Mapper}
sealed abstract class ClassAccessors[C] {
def apply(c: C): Map[Symbol, FieldDef]
}
trait LowPriorClassInfo extends Poly1 {
implicit def fieldInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], info: TypeInfo[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, PlainField(f, info)))
}
object classInfo extends LowPriorClassInfo {
implicit def recurseInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], acc: ClassAccessors[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, EmbeddedType(acc(f))))
}
implicit def provideClassAccessors[C, L <: HList, ML <: HList]
(implicit lgen: LabelledGeneric.Aux[C, L],
map: Mapper.Aux[classInfo.type, L, ML],
toList: ToTraversable.Aux[ML, List, (Symbol, FieldDef)]) =
new ClassAccessors[C] {
def apply(c: C) = toList(map(lgen.to(c))).toMap
}
def classAccessors[C](c: C)(implicit acc: ClassAccessors[C]) = acc(c)
now
classAccessors(User(100, "Miles Sabin", Contact("+1 234 567 890", "Earth, TypeLevel Inc., 10")))
will result to
Map(
'id -> PlainField(100, NumericField( scala.math.Numeric$LongIsIntegral$#...)),
'name -> PlainField("Miles Sabin", StringField),
'contact -> EmbeddedType( Map(
'phone -> PlainField( "+1 234 567 890", StringField),
'address -> PlainField("Earth, TypeLevel Inc., 10", StringField))))