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))))
Related
Let's say I have a trait like:
trait MyTrait[T, U <: SomeParentClass] {
def get(data: T): Option[U]
}
and a concrete implementation like:
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
To simplify things, let's also say we have the following types for U <: SomeParentClass:
TypeA
TypeB
TypeC
and some functions:
def str2TypeA(s: String): Option[TypeA] = ...
def str2TypeB(s: String): Option[TypeB] = ...
def str2TypeC(s: String): Option[TypeC] = ...
Then let's say I have:
val mySeq = Seq(
MyStringClass(str2TypeA),
MyStringClass(str2TypeB),
MyStringClass(str2TypeC)
)
What I want to do is filter mySeq based on the return type U. Something like:
mySeq.collect { case a: Function1[_, Option[TypeA]] => a}
I'm running into type erasure issues as expected. I'm curious what approaches might work well here to achieve my goal of filtering based on the type U.
You would usually use a TypeTag to handle cases where erasure gets in your way. They are essentially simple objects that contain all of the information available at compile-time, and they're easy to obtain either by requesting an implicit parameter, or just writing typeTag[T]. For example:
import scala.reflect.runtime.universe._
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])(implicit val utt: TypeTag[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
mySeq.collect {
case a: MyStringClass[Option[TypeA]] if (a.utt == typeTag[Option[Type[A]]) => a
}
I have some case classes which have a method tupled defined in its companion object. As it can be seen from the code below in companion objects, it is just code duplication.
case class Book(id: Int, isbn: String, name: String)
object Book {
def tupled = (Book.apply _).tupled // Duplication
}
case class Author(id: Int, name: String)
object Author {
def tupled = (Author.apply _).tupled // Duplication
}
From another question (can a scala self type enforce a case class type), it seems like we can not enforce the self-type of a trait to be a case class.
Is there a way to define a trait (say Tupled) that can be applied as following?
// What would be value of ???
trait Tupled {
self: ??? =>
def tupled = (self.apply _).tupled
}
// Such that I can replace tupled definition with Trait
object Book extends Tupled {
}
Because there's no relationship between FunctionN types in Scala, it's not possible to do this without arity-level boilerplate somewhere—there's just no way to abstract over the companion objects' apply methods without enumerating all the possible numbers of members.
You could do this by hand with a bunch of CompanionN[A, B, C, ...] traits, but that's pretty annoying. Shapeless provides a much better solution, which allows you to write something like the following:
import shapeless.{ Generic, HList }, shapeless.ops.product.ToHList
class CaseClassCompanion[C] {
def tupled[P <: Product, R <: HList](p: P)(implicit
gen: Generic.Aux[C, R],
toR: ToHList.Aux[P, R]
): C = gen.from(toR(p))
}
And then:
case class Book(id: Int, isbn: String, name: String)
object Book extends CaseClassCompanion[Book]
case class Author(id: Int, name: String)
object Author extends CaseClassCompanion[Author]
Which you can use like this:
scala> Book.tupled((0, "some ISBN", "some name"))
res0: Book = Book(0,some ISBN,some name)
scala> Author.tupled((0, "some name"))
res1: Author = Author(0,some name)
You might not even want the CaseClassCompanion part, since it's possible to construct a generic method that converts tuples to case classes (assuming the member types line up):
class PartiallyAppliedProductToCc[C] {
def apply[P <: Product, R <: HList](p: P)(implicit
gen: Generic.Aux[C, R],
toR: ToHList.Aux[P, R]
): C = gen.from(toR(p))
}
def productToCc[C]: PartiallyAppliedProductToCc[C] =
new PartiallyAppliedProductToCc[C]
And then:
scala> productToCc[Book]((0, "some ISBN", "some name"))
res2: Book = Book(0,some ISBN,some name)
scala> productToCc[Author]((0, "some name"))
res3: Author = Author(0,some name)
This will work for case classes with up to 22 members (since the apply method on the companion object can't be eta-expanded to a function if there are more than 22 arguments).
The problem is that the signature of apply differs from case to case, and that there is no common trait for these functions. Book.tupled and Author.tupled basically have the same code, but have very different signatures. Therefore, the solution may not be as nice as we'd like.
I can conceive of a way using an annotation macro to cut out the boilerplate. Since there isn't a nice way to do it with the standard library, I'll resort to code generation (which still has compile-time safety). The caveat here is that annotation macros require the use of the macro paradise compiler plugin. Macros must also be in a separate compilation unit (like another sbt sub-project). Code that uses the annotation would also require the use of the macro paradise plugin.
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
#compileTimeOnly("enable macro paradise to expand macro annotations")
class Tupled extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro tupledMacroImpl.impl
}
object tupledMacroImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val result = annottees map (_.tree) match {
// A case class with companion object, we insert the `tupled` method into the object
// and leave the case class alone.
case (classDef # q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
:: (objDef # q"object $objName extends { ..$objEarlyDefs } with ..$objParents { $objSelf => ..$objDefs }")
:: Nil if mods.hasFlag(Flag.CASE) =>
q"""
$classDef
object $objName extends { ..$objEarlyDefs } with ..$objParents { $objSelf =>
..$objDefs
def tupled = ($objName.apply _).tupled
}
"""
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a companion object of a case class.")
}
c.Expr[Any](result)
}
}
Usage:
#Tupled
case class Author(id: Int, name: String)
object Author
// Exiting paste mode, now interpreting.
defined class Author
defined object Author
scala> Author.tupled
res0: ((Int, String)) => Author = <function1>
Alternatively, something like this may be possible with shapeless. See #TravisBrown's better answer.
Given this case class case class Location(id: BigInt, lat: Double, lng: Double)
and a list of strings List("87222", "42.9912987", "-93.9557953")
I would like to do something like Location.fromString(listOfString) in a way Location instance is created with string types converted into appropriate types.
Only way I can think of is define fromString method in each case class but I'm looking for a more generic solution each class can inherit. For example, I would have case class Location(...) extends Record, where Record implements fromString that does conversion based on argument types defined in Location class.
Normally for best performance in such you need to create some macro.
Luckily there is beautiful shapeless library with which you could create such generic reader with near-macro performance in no time using it's generic case class representation
First define typeclass and some instances for reading your fields:
trait Read[T] {
def fromString(s: String): T
}
implicit object readBigInt extends Read[BigInt] {
def fromString(s: String): BigInt = BigInt(s)
}
implicit object readDouble extends Read[Double] {
def fromString(s: String): Double = s.toDouble
}
Next define your main typeclass:
trait ReadSeq[T] {
def fromStrings(ss: Seq[String]): T
}
Now it's shapeless time (thinking about Dalí?) . We create reader for powerful Heterogenous List, which is almost like List but with of statically known length and element types.
It's no harder than to match simple List. Just define cases for empty list and cons:
import shapeless._
implicit object readHNil extends ReadSeq[HNil] {
def fromStrings(ss: Seq[String]) = HNil
}
implicit def readHList[X, XS <: HList](implicit head: Read[X], tail: ReadSeq[XS]) =
new ReadSeq[X :: XS] {
def fromStrings(ss: Seq[String]) = ss match {
case s +: rest => (head fromString s) :: (tail fromStrings rest)
}
}
Now we can use out-of-the-box macro mapping of HLists and case classes via Generic:
implicit def readCase[C, L <: HList](implicit gen: Generic.Aux[C, L], read: ReadSeq[L]) =
new ReadSeq[C] {
def fromStrings(ss: Seq[String]) = gen.from(read.fromStrings(ss))
}
Finally we could build our typeclass user:
def fromStringSeq[T](ss: Seq[String])(implicit read: ReadSeq[T]) = read.fromStrings(ss)
from this point having
case class Location(id: BigInt, lat: Double, lng: Double)
val repr = List("87222", "42.9912987", "-93.9557953")
you can ensure that
fromStringSeq[Location](repr) == Location(BigInt("87222"), 42.9912987, 93.9557953)
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.
Is it possible to provide a generic function which would traverse an arbitrary case class hierarchy and collect information from selected fields? In the following snippet, such fields are encoded as Thing[T].
The snippet works fine for most scenarios. The only problem is when Thing wraps a type class (e.g. List[String]) and such field is nested deeper in the hierarchy; when it is on the top level, it works fine.
import shapeless.HList._
import shapeless._
import shapeless.ops.hlist.LeftFolder
case class Thing[T](t: T) {
def info: String = ???
}
trait Collector[T] extends (T => Seq[String])
object Collector extends LowPriority {
implicit def forThing[T]: Collector[Thing[T]] = new Collector[Thing[T]] {
override def apply(thing: Thing[T]): Seq[String] = thing.info :: Nil
}
}
trait LowPriority {
object Fn extends Poly2 {
implicit def caseField[T](implicit c: Collector[T]) =
at[Seq[String], T]((acc, t) => acc ++ c(t))
}
implicit def forT[T, L <: HList](implicit g: Generic.Aux[T, L],
f: LeftFolder.Aux[L, Seq[String], Fn.type, Seq[String]]): Collector[T] =
new Collector[T] {
override def apply(t: T): Seq[String] = g.to(t).foldLeft[Seq[String]](Nil)(Fn)
}
}
object Test extends App {
case class L1(a: L2)
case class L2(b: Thing[List[String]])
implicitly[Collector[L2]] // works fine
implicitly[Collector[L1]] // won't compile
}
I'm afraid that this is impossible. HList appears to be constructed compile time from statically known things. So, when you wrap your types, for whatever reason, it seems that the HList is unable to infer the proper implicits.
Here is a simple example built from shapeless's flatten example.
object Test extends App {
import shapeless._
import ops.tuple.FlatMapper
import syntax.std.tuple._
trait LowPriorityFlatten extends Poly1 {
implicit def default[T] = at[T](Tuple1(_))
}
object flatten extends LowPriorityFlatten {
implicit def caseTuple[P <: Product](implicit fm: FlatMapper[P, flatten.type]) =
at[P](_.flatMap(flatten))
}
case class AT[T](a: T, b: T)
case class A2T[T](a: AT[T], b: AT[T])
case class A2(a: AT[Int], b: AT[Int])
println(flatten(A2T(AT(1, 2), AT(3, 4))))
println(flatten(A2(AT(1, 2), AT(3, 4))))
}
You would think that this should print out the same thing for A2T and A2, however it does not. It actually prints out:
(1,2,3,4)
(AT(1,2),AT(3,4))
So, I do not think you can use Shapeless to do what you want.
However! You can still walk your case class hierarchy looking for Things (just not with shapeless). Check this out!
object Test extends App {
case class Thing[T](t: T) {
def info: String = toString
}
def collect[T](t: T): Iterator[String] = t match {
case t: Thing[_] => Iterator(t.info)
case p: Product => p.productIterator.flatMap(collect)
case _ => Iterator()
}
case class L0(a: L1)
case class L1(a: L2)
case class L2(a: Thing[List[String]])
case class MT(a: L2, b: L2, c: Thing[Int])
println("Case #1")
collect(L0(L1(L2(Thing(List("a", "b", "c")))))).foreach(println)
println("Case #2")
collect(MT(L2(Thing(List("a", "c"))), L2(Thing(List("b"))), Thing(25))).foreach(println)
}
This has output:
Case #1
Thing(List(a, b, c))
Case #2
Thing(List(a, c))
Thing(List(b))
Thing(25)