I have a List of a parametric type, List[Field[_]], and I want something like this:
sealed trait FieldType[K] { val name: String }
sealed trait StringField extends FieldType[String]
case object publisher extends StringField { val name = "publisher" }
// ...
trait Field[K] {
val field: FieldType[K]
val value: K
}
case class Publisher(value: String) extends Field[String] { val field = publisher }
// ...
def get[K](l: List[Field[_]], key: FieldType[K]) : Option[K] =
l match {
case Nil => None
case (a:Field[K]) :: rest => Some(a.value)
case (a:Field[_]) :: rest => get(rest, key)
}
which doesn't work, because K is erased. I tried typetags, but I must confess I got lost. Any quick way to get what I want?
Yes, you can use type tags to get the compiler to generate the erased type information. According to the Scala documentation there are three ways to optain a tag.
By adding an implicit evidence parameter of type ClassTag, the compiler will generate the missing type information if it cannot find a suitable implicit value. Given the evidence, we can obtain the runtime class and compare it to the runtime class of the value of your Field.
def get[A](l: List[Field[_]])(implicit evidence: ClassTag[A]): Option[A] =
l match {
case Nil => None
case (a: Field[A]) :: rest if evidence.runtimeClass == a.value.getClass =>
Some(a.value)
case a :: rest =>
get[A](rest)
}
However, note that in this example, get[Int] will produce a ClassTag with the runtime class Int whereas value.getClass will return a java.lang.Integer
Given case class Other(value: Int) extends Field[Int], this will yield the following result:
def main(args: Array[String]): Unit = {
println(get[String](Other(4) :: Other(2) :: Publisher("foo") :: Nil)) // Some(foo)
println(get[Integer](Other(4) :: Other(2) :: Publisher("foo") :: Nil)) // Some(4)
println(get[Int](Other(4) :: Other(2) :: Publisher("foo") :: Nil)) // None
}
Note that I removed the parameter key, as it did not serve a purpose. If the key is supposed to be unique, then, instead of checking the type tag, I would suggest matching against the key, thereby not using reflection:
def get[A](l: List[Field[_]], key: FieldType[A]): Option[A] = l match {
case Nil => None
case a :: rest if a.field == key => Some(a.value.asInstanceOf[A])
case a :: rest => get(rest, key)
}
Edit: To address your questions from the comments:
is the asInstanceOf necessary? ... I was under the impression that having to resort to it is sort of bad practice/to be avoided/unsafe. Is that correct?
Since l is of type List[Field[_]], a is of type Field[_], meaning that a.value has the existential type, i.e., the compiler does not know it is of type A. However, because of the relation between a.key and a.value, we know that if a.field is of type FieldType[A], then value is of type A, so this particular type cast is safe (as long as you do not change the code).
You are absolutely correct that having to use a type cast indicates a flaw in the design, so maybe the best solution is to redesign Field, or the list l. Actually, I'd ask myself why you need to put various different types of Field in a list and then extract a certain type of Field later on? Maybe List is the wrong data structure? Would a Map[FieldType[_], List[Field[_]] be a better choice to store your fields? Or a Map[Class[_], List[Field[_]]? Or maybe a custom data structure?
Note that you can get rid of the asInstanceOf in the following way:
def get[A](l: List[Field[_]], key: FieldType[A]): Option[A] = l match {
case Nil => None
case (a: Field[A]) :: rest if a.field == key => Some(a.value)
case a :: rest => get(rest, key)
}
but this does not give you more static safety, because type erasure makes Field[A] match against any generic version of Field. I'd say this variant is worse because it makes the explicit type cast implicit, hence this code is more error prone.
I was trying to use TypeTag[] and typeOf[] instead of ClassTag and runtimeClass, as those where the example I found on the documentation page you linked (and elsewhere). What's the difference? Most of all, I want information regarding the _ in Field[_], so why oh why the ClassTag is on A?!?
There are three type tags that were introduced as a replacement for Manifest:
TypeTag
WeakTypeTag
ClassTag
From the ScalaDoc:
ClassTags are a weaker special case of scala.reflect.api.TypeTags#TypeTags, in that they wrap only the runtime class of a given type, whereas a TypeTag contains all static type information.
While you could probably also solve the problem using TypeTag, I figured that it suffices to compare the runtime class.
The problem is that the existential type can really be any superclass of A, because the list l can contain all kinds of Fields. In the example I gave, we have a list of Field[Int] :: Field[Int] :: Field[String] :: Field[Int], hence the existential type in this case must be Any, the least common supertype of Int and String. In other words: You benefit nothing from deriving the existential type of l: List[Field[_]].
However, what you actually want to do is find the first element in the list whose value is of type A. Because A is erased, the only way to obtain information about its runtime type is by passing the information as an additional argument, e.g., using the implicit ClassTag evidence. Now all that remains is finding out which element has a value of the corresponding type, hence a.value.getClass == evidence.runtimeClass.
Related
I'm trying to define some structure like this
case class Transformer[From, To](
name: String,
get: PaymentEvent => From,
to: From => To
I want to filter elements with names that are part of a Set
class filterName(names: Set[String]) extends lowPriority {
implicit def get[From, To] = at[Transformer[From, To]]{ trans =>
if (names.contains(trans.name))
trans :: HNil
else
HNil
}
}
This is the concrete value:
type HTransformer = Transformer[String, String] :: Transformer[Long, Long] :: HNil
When I want to apply the function to the value
private def filter(fields: HTransformer, names: Set[String]): HTransformer = {
fields.flatMap(new filterName(names))
}
The compiler reports errors:
Error:(42, 19) could not find implicit value for parameter mapper: shapeless.ops.hlist.FlatMapper[f.type,shapeless.::[com.ingenico.datalake.Transformer[String,String],shapeless.::[com.ingenico.datalake.Transformer[Long,Long],shapeless.HNil]]]
fields.flatMap(new filterName(names))
I guess you misunderstand type-level calculations.
If you want to filter an hlist depending on whether an element is a part of the Set then you must know this (if an element is a part of the Set) at compile time but actually you know this only at runtime. So filterName will not work.
For example you can transform the hlist to a list and filter it as an ordinary collection at runtime.
I have this type which will be generated via shapeless:
type hlistt = STUDENT.type :: AUTO_LOANS.type :: HNil
Basically I have a bunch of case objects extending a trait so I managed to create a method which gives me instances of all case objects as an HList
Then using import shapeless.ops.hlist.Last and init I wrote a method to retrieve one of the nodes in the HList if the value is equal to the string "student":
def getLast(hl:hlistt) = {
val last0=Last[hlistt]
val la=last0(hl)
if (la.value == "student") la
else init(hl)
}
The issue is that if I call this method I will not get the correct node type from the HList.
getLast(STUDENT :: AUTO_LOANS :: HNil)
The method works and returns the node but the type is off:
Product with Serializable = STUDENT :: HNil
Do I need some Witness/Aux implicits to return the correct type?
la is of type AUTO_LOANS.type, init(hl) is of type STUDENT.type :: HNil, so
if (la.value == "student") la
else init(hl)
is of type Any (or Product with Serializable).
If you would like to return values of different types from different branches you need a Poly.
import shapeless.{Poly1, Witness}
object myPoly extends Poly1 {
implicit def studentCase: Case.Aux[Witness.`"student"`.T, STUDENT.type] =
at(_ => STUDENT)
implicit def autoLoansCase: Case.Aux[Witness.`"auto-loans"`.T, AUTO_LOANS.type] =
at(_ => AUTO_LOANS)
}
import shapeless.syntax.singleton._
println(
myPoly("student".narrow)
) // STUDENT
println(
myPoly("auto-loans".narrow)
) // AUTO_LOANS
// println(
// myPoly("abc".narrow)
// ) // doesn't compile
This approach works if the string is known at compile time.
I am not quite sure what you want to do. Given:
type hlistt = STUDENT.type :: AUTO_LOANS.type :: HNil
Last[hlistt] will resolve to AUTO_LOANS.type (your true if branch)
while init will resolve to STUDENT :: HNil (your false if branch)
the LUB (least upper bound) of these types will be Product with Serializable so that's why you see that.
If you want to check a runtime property of a member of the hlist you'll have to thread the appropriate typebounds and result types through by deriving them with the appropriate machinery. In this case that is already given by shapeless.
https://scalafiddle.io/sf/fdtn3cz/0
Is this what you wanted?
EDIT:
I also just read
I have this type which will be generated dynamically:
what do you exactly mean by "dynamically"? because unless you can specify some compile time properties, shapeless might not be the solution you're looking for.
I'm trying to come up with a generic function that can take primitive data types and also other objects that extends scala.math.Ordering.
I checked How to get to type parameters of a reflect.runtime.universe.Type in scala?
and I want to get the type of a variable at runtime and I could't get my answer.
Example below:
import scala.reflect.runtime.universe._
import scala.collection.SortedSet
def getTypeTag[T: TypeTag](obj: T) = typeTag[T]
def createSortedSet[A: TypeTag](y: A) = typeOf[A] match{
//handle the primitive data types
case t1 if t1 =:= typeOf[Int] =>
SortedSet(y)(implicitly[Ordering[Int]].reverse)
case t2 if t2 =:= typeOf[Long] =>
SortedSet(y)(implicitly[Ordering[Long]].reverse)
.....
// last part handle any object that implements Comparable
case tx if tx.getClass.getInterfaces.exists( _.toString == "interface java.lang.Comparable") =>
{// trying to get the type of the variable and then pass in to the Ordering
val TT: Type = getTypeTag(t).tpe
SortedSet(y)(implicitly[Ordering[TT]].reverse)}
case _ => ...
}
so in order to pass in a object and let the Ordering do its job, I need to pass in the type to the Ordering, Say I create a case class that extends Ordering
case class Value(i: Int) extends Ordered[Value] {def compare(that: Value) = this.i - that.i}
val v1 = Value(3)
// now I want to get a SortedSet instance by calling the createSortedSet
createSortedSet(v1)
I got error when I made the above call, so I think reflect.runtime.universe.Type is not really can be used as Type. Is my approach wrong?
You can make it much simpler:
def sortedSet[T : Ordering](t:T) = SortedSet(t)(implicitly[Ordering[T]].reverse)
That works for the use case you describe, don't need to differentiate between primitives and other objects, or get the actual type at runtime. When calling this function, the compiler will make sure there's an Ordering available for the type being used (Or fail if there's not).
In your case with Value, he will implicitly provide one, based on your implementation of Ordered
I've got a sequence Seq[Any] that has a variety of objects in it (like String, Integer, List[String], etc). I'm trying to sift through the list and break it up into separate lists partitioned based on the class type. The following is a pattern I'm using in the code:
val allApis = mySequence.filter(_.isInstanceOf[String])
This works well and doesn't generate any warnings. However, when I try to do the same for filtering out the objects that are Lists of strings:
val allApis = mySequence.filter(_.isInstanceOf[List[String]])
I get a warning that says non-variable type argument String in type List[String] is unchecked since it is eliminated by erasure. Now, the technique actually works and I'm able to comfortably filter the sequence as desired, but I'm wondering what is the appropriate way to deal with the warning in an idiomatic way so that I know I don't have a serious bug lurking in the background waiting to blow up
It doesn't work because it will pick out List[Double] or any other list in addition to List[String]. There are a variety of ways of fixing the problem, including wrapping any parameterized types in a non-parameterized case class:
case class StringList(value: List[String])
and then you can just
mySequence.collect{ case StringList(xs) => xs }
to pull out the lists of strings (with the correct type, and type-safely also).
Alternatively, if you want to not wrap objects and want to be sure that they're of the correct type, you can check every element:
mySequence.filter( _ match {
case xs: List[_] => xs.forall( _ match { case _: String => true; case _ => false })
case _ => false
})
though even this won't let you know which type empty lists were supposed to be.
Another possibility is to glue TypeTags to everything in your list; this will prevent you needing to manually wrap things. For instance:
import scala.reflect.runtime.universe.{TypeTag, typeTag}
def add[A](xs: List[(Any, TypeTag[_])], a: A)(implicit tt: TypeTag[A]) = (a, tt) :: xs
val mySequence = add(add(add(Nil, List(42)), true), List("fish"))
mySequence.filter(_._2.tpe weak_<:< typeTag[List[String]].tpe)
val v = 1 ::"abc" :: true :: Nil
v : List[Any] = List(1,abc,true)
type parameter of List type has been unified to the greatest common super type of the elements in the List which is Any.
Shapeless is to the rescue.
import shapeless._
import HList._
val s = 1 :: "abc" :: true: HNil
s : shapeless.::[Int,shapeless.::[String,shapelsss.::[Boolean,shapeless.HNil]]]
= 1 :: abc :: true :: HNil
With Shapeless HList you can get compile time safety for a heterogeneous list. you can now filter in a typesafe manner. e.g.
s.filter[String]
if i have a class that accepts a Type argument for example Seq[T] , and i've many objects of this class. and i want to split them depending on type Argument T
for example :
val x = List(Seq[Int](1,2,3,4,5,6,7,8,9,0),Seq[String]("a","b","c"))
x.foreach { a =>
a match{
case _ : Seq[String] => print("String")
case _ : Seq[Int] => print("Int")
}
}
the result of this code is StringString.
it only matches the class Seq not the Type also , what should i do to force it to match the Type ?
What you're seeing happens due to Type Erasure (http://docs.oracle.com/javase/tutorial/java/generics/erasure.html), some IDEs can warn you for errors like these.
You could have a look at Manifests, for example check out What is a Manifest in Scala and when do you need it?
Edit: like Patryk said, TypeTag replaced Manifest in Scala 2.10, see Scala: What is a TypeTag and how do I use it?
TypeTag Approach
The Java runtime requires generic type param erasure. Scala compiler combats this by 'injecting' type info into methods declared with TypeTag type arg:
def typeAwareMethod[T: TypeTag] (someArg: T) {
... // logic referring to T, the type of varToCheck
}
(alternatively, can use an equivalent, more long-winded implicit param - not shown)
When scala compiles an invocation of a method having (1) type arg [T: TypeTag] and (2) normal arg someArg: T, it collects type param metadata for someArg from the calling context and augments the type arg T with this metadata. T's value plus tag data are externaly type-inferred from calls:
val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)
Logic Referring to T (within above method) - runtime reflection
import scala.reflection.runtime.universe._ : contents of the universe object of type scala.relection.api.JavaUniverse. NB: subject to evolutionary API change
Direct TypeTag comparison:
The tag info can be obtained via method typeTag[T], then directly tested/pattern matched for (exact) equality with other type tags:
val tag: TypeTag[T] = typeTag[T]
if (typeTag[T] == typeTag[List[Reptile]]) ...
typeTag[T] match {
case typeTag[List[Reptile]] => ...
}
Limitations: not subtype aware (above won't match List[Frog]); no additional metadata obtainable through TypeTag.
Smarter Type-comparison operations:
Convert to Type via typeOf[T] (or typeTag[T].tpe). Then use the gammut of Type ops, including pattern-matching. NB: in reflection typespace, =:= means type equivalance (analogue of :), <:< means type conformance (analogue of <:)
val tType: Type = typeOf[T] // or equivalently, typeTag[T].tpe
if (typeOf[T] <:< typeOf[List[Reptile]]) ... // matches List[Frog]
typeOf[T] match {
case t if t <:< typeOf[List[Reptile]] => ...
}
// Running Example:
def testTypeMatch[T: TypeTag](t: T) = if (typeOf[T] <:< typeOf[Seq[Int]]) "yep!!!"
test(List[Int](1, 2, 3)) // prints yep!!!
Method still needs type param [T: TypeTag] or you'll get the type-erasure view of the world...
Introspect on Type metadata
I lied in 2 ;). For your case, typeOf[T] actually returns TypeRef (a subtype of Type), since you're instantiating a type declared elsewhere. To get at the full metadata, you need to convert Type to TypeRef.
typeTag[T].tpe match {
case t: TypeRef => ... // call t.args to access typeArgs (as List[Type])
case _ => throw IllegalArgumentException("Not a TypeRef")
}
instead of t: TypeRef, can extract parts via pattern match on:
case TypeRef(prefixType, typeSymbol, typeArgsListOfType) =>
Type has method:
def typeSymbol: Symbol
Symbol has methods:
def fullName: String
def name: Name
Name has methods:
def decoded: String // the scala name
def encoded: String // the java name
Solution For Your Case
Solution based on (3):
import scala.reflect.runtime.universe._
def typeArgsOf[T: TypeTag](a: T): List[Type] = typeOf[T] match {
case TypeRef(_, _, args) => args
case _ => Nil
}
val a = Seq[Int](1,2,3,4,5,6,7,8,9,0)
val b = Seq[String]("a","b","c")
// mkString & pring for debugging - parsing logic should use args, not strings!
print("[" + (typeArgsOf(a) mkString ",") + "]")
print("[" + (typeArgsOf(b) mkString ",") + "]")
Aside: there's an issue with this test case:
val x = List(Seq[Int](1,2,3,4,5,6,7,8,9,0),Seq[String]("a","b","c"))
Type of x is List[Seq[Any]]. Any is the lowest common ancestor of String and Int. In this case there's nothing to introspect, since all types descend from Any , and there's no further type information available. To get stronger typing, separate the two Seqs, either via separate variables or a tuple/pair - but once separated, no higher order common mapping / folding across the two. "Real world" cases shouldn't have this problem.
I would argue it's equally sensible to def the logic with multiple prototypes one per sequence type, than go into those type-erasure workarounds. The 2.10 compiler doesn't warn about type erasure, and at runtime it seems to work well in my case.
Presumably this avoids the problem, producing more intelligible code.