I have a method that should dynamically cast a field member into a certain type depending on a configuration flag format.
This flag format, accepts one of the following type values:
object Types {
val Str = "string"
val IntNum1 = "int"
val IntNum2 = "integer"
val DoubleNum = "double"
val LongNum = "long"
}
One option would be to use reflection. Another option would be to use pattern matching (the method I'm trying to do this with)
Here's the method for String types:
private def getValClassIfStr(format: String): Class[String] = {
format.toLowerCase match {
case Types.Str => classOf[String]
case _ => null
}
}
And Here's the method for Numerical types, extending from AnyVal, that fails to compile:
private def getValueClass[T <: AnyVal](format: String): Try[Class[T]] = {
format.toLowerCase match {
case Types.IntNum1 || Types.IntNum2 => Success(classOf[Int])
case Types.DoubleNum => Success(classOf[Double])
case Types.LongNum => Success(classOf[Long])
case _ => Failure(new Exception)
}
}
The second method doesn't compile because of a:
Type mismatch: required Try[Class[T]] found Try[Class[Int]]
Which I don't understand, given T <: AnyVal.
Any idea? and if that's a bad approach, what would a "clean" alternative be?
I think the reason why this doesn't work becomes clearer once you replace Class[T] in the return value with Class[AnyVal]:
type mismatch;
found : Class[Int](classOf[scala.Int])
required: Class[AnyVal]
Note: Int <: AnyVal, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: AnyVal`. (SLS 3.2.10)
In other words, any call to getValueClass[T] would always have to be populated with T = AnyVal by the compiler, since format is only evaluated at runtime. This, however, is not possible since Class[T] is invariant, so you can't return a Class[Int] and a Class[Double] as a Class[AnyVal], since for invariant containers there's no subtyping relationship.
To demonstrate:
var x: Class[AnyVal] = _
x = classOf[Int]
// Boom
Related
Suppose Lofty is a sealed trait and Earthy is one of its case classes. In a match such as this:
loftyItem match {
...
case e # Earthy(x,y,z) => { foo(e) }
...
}
where foo expects an Earthy as argument, the compiler melts down because e is inferred only to be of type Lofty. I can work around this, but it doesn't fit my model of how things ought to be. (I'm working with Scala 2.13.5.) Can someone explain why the compiler's behavior makes sense, and make me happy with Scala again?
In response to comments, let me be more precise:
object QTest {
trait Ethereal
case class Lofty[A <: Ethereal](val slotMaybe: Option[A]) {
}
class Earthy(val field: Int) extends Ethereal
object Earthy {
def apply(fx: Int): Earthy = {
new Earthy(fx)
}
def unapply(x: Ethereal): Option[Int] = x match {
case y: Earthy => Some(y.field)
case _ => None
}
}
def testReally(just: Lofty[Ethereal]):
Lofty[Earthy] = {
just.slotMaybe match {
case Some(c) => c match {
case cnfC # Earthy(i) => {
just.copy(slotMaybe = Some(cnfC))
}
case _ => throw new RuntimeException("oops")
}
case _ => throw new RuntimeException("oops")
}
}
}
Compiling this produces the error:
QTest.scala:25: error: type mismatch;
found : QTest.Ethereal
required: QTest.Earthy
just.copy(slotMaybe = Some(cnfC))
I obviously jumped to a conclusion, but the full example seems to have the same issue. Why does the compiler infer type Ethereal for cnfC instead of Earthy? Even if the compiler gets the type right for most uses of #, why does it get it wrong here?
SLS 8.1.3 Pattern Binders states
A pattern p implies a type T if the pattern matches only values of the type T.
The pattern Earthy(i) in
case cnfC # Earthy(i) =>
represents extractor pattern meaning it will match according to your definition of unapply which is
object Earthy {
def unapply(x: Ethereal): Option[Int] = ???
}
Because the declared type of x is wider Ethereal instead of narrower Earthy it will not match
... only values of the type T
where T = Earthy, but instead it can match other subtypes of Ethereal as well. Hence the compiler can only be sure it will be some Ethereal.
If you wish to make it compile with the extractor pattern then either declare your unapply as
object Earthy {
def unapply(x: Earthy): Option[Int] = ???
}
or better yet use case class instead which gets a correct unapply automagically
case class Earthy(field: Int) extends Ethereal
In my project, I need to write a generic class, which in a single method handles some types with its handler in a special way (Numeric is used for clarity in the example).
class A[T](val a:T){
def doSomething(b:T):T = a match{
case a : Int => doSomethingWithIntOrDouble(b)
case a : Double => doSomethingWithIntOrDouble(b)
case _ => b
}
def doSomethingWithIntOrDouble(b:T)(implicit ev:Numeric[T]):T =
ev.plus(a,b)
}
<console>:13: error: could not find implicit value for parameter ev: Numeric[T]
case a : Int => doSomethingWithIntOrDouble(b)
^
<console>:14: error: could not find implicit value for parameter ev: Numeric[T]
case a : Double => doSomethingWithIntOrDouble(b)
I think this happens because the compiler picks up the type parameter but not the actual one. Tell me, is there any way around this?
PS Okay If we assume that the answer is correct, then it is necessary to overload the dosomething method to achieve polymorphism.
class A[T](val a:T){
def doSomething(b:T)(implicit ev:Numeric[T]):T = ev.plus(a,b)
def doSomething(b:T):T = b
}
But in this case, another problem arises.
scala> a.doSomething(2)
<console>:13: error: ambiguous reference to overloaded definition,
both method doSomething in class A of type (b: Int)Int
and method doSomething in class A of type (b: Int)(implicit ev: Numeric[Int])Int
match argument types (Int)
a.doSomething(2)
I am not completely sure this is want your want, but I hope it helps.
Basically, you need to forward the evidence that the T type is a Numeric to the outer method. But, you also have to handle the case where it is not.
For that case, you can provide a default value for the implicit parameter like this:
class A[T](val a: T) {
def doSomething(b: T)(implicit ev: Numeric[T] = null): T = Option(ev) match {
case Some(ev) => doSomethingWithNumeric(b)(ev)
case None => b
}
def doSomethingWithNumeric(b: T)(implicit ev: Numeric[T]): T =
ev.plus(a, b)
}
It seems to work.
(new A(10)).doSomething(100) // res: Int = 110
(new A("hey")).doSomething("world") // res: String = "world"
Note that, if you will have many methods, maybe a cleanest solution would be to make A a trait with two implementations, one for numeric types and other for no numeric types.
Make the constructors of both sub classes private and create a factory for A in the companion object which ask for the implicit numeric parameter, and if found it will return a new instance of the numeric subclass.
This code compiles with an error:
def f1[T](e: T): T = e match {
case i:Int => i
case b:Boolean => b
}
// type mismatch;
// found : i.type (with underlying type Int)
// required: T
// case i:Int => i ...
And this code implementing GADT looks pretty identical from type checking perspective, but compiles without error:
sealed trait Expr[T]
case class IntExpr(i: Int) extends Expr[Int]
case class BoolExpr(b: Boolean) extends Expr[Boolean]
def eval[T](e: Expr[T]): T = e match {
case IntExpr(i) => i
case BoolExpr(b) => b
}
In both cases inside pattern matching expression we know that i and b are Int and Boolean. Why compilation failed on first example and succeeded on the second one?
The first case is unsound because you underestimate the variety of types in Scala type system. It would make sense if, when we took case i:Int branch we knew T was Int, or at least a supertype of Int. But it doesn't have to be! E.g. it could be 42.type or a tagged type.
There's no such problem in the second case, because from IntExpr <: Expr[T], the compiler does know T must be exactly Int.
You ask of your function to return a type T, then you pattern-match against Int and Boolean.
Except your function has no evidence that Int and Boolean are also of type T: when you pattern-match, you introduce the constraint that Int <: T and Boolean <: T.
You could either replace the return type T by a fixed type like String and return a String, or introduce a constraint that will satisfy both the case Int and Boolean.
//this compiles
def f1[T](e: T ): String = e match {
case _:Int => "integer"
case _:Boolean => "boolean"
}
//this compiles too, but will return AnyVal
def f1[T >: AnyVal](e: T ): T = e match {
case i:Int => i
case b:Boolean => b
}
Basically you can't just return any type T dynamically because you need to prove at compile time that your function type-checks out.
The other function in your example avoids the issue by encapsulating type constraints within case classes IntExpr <: Expr[Int] and BoolExpr <: Expr[Boolean] (notice how Expr[_] would be the equivalent of T in the constraints I mentioned above). At compile time, T is properly identified in all cases (e.g in the IntExpr you know it's an Int)
In addition to #Esardes answer, this worked by defining a type bound for T:
scala> def f1[T >: AnyVal](e: T):T = e match {
| case i:Int => i
| case b:Boolean => b
| }
f1: [T >: AnyVal](e: T)T
scala> f1(1)
res3: AnyVal = 1
scala> f1(true)
res4: AnyVal = true
I have a trait that defines an associated type. In a function, I want to return this associated type which is different depending on the supplied value:
sealed trait Abstract {
type T
}
class Impl1 extends Abstract {
type T = Int
}
class Impl2 extends Abstract {
type T = Boolean
}
object G {
def get[A <: Abstract] (x: A): A#T = {
x match {
case i1: Impl1 =>
5
case i2: Impl2 =>
true
}
}
}
The problem is, that scala doesn't recognize that in the matched case A#T is Int or Boolean respectively and I get the following error message:
Expression of type Int doesn't conform to expected type A#T
How can I solve this?
Your match condition gives AnyVal result, it know nothing about A#T. You should use .asInstanceOf[A#T] on result or extrac pattern matching
val z = x match {
case i1: Impl1 =>
5
case i2: Impl2 =>
true
}
z match {
case x: A#T => x
}
The problem is, that scala doesn't recognize that in the matched case A#T is Int or Boolean respectively
Strictly speaking, it isn't. For example, you could call
get[Abstract](new Impl1)
in which case A#T is Abstract#T which is neither Int not Boolean. Or
get(throw new Exception)
so you get Nothing#T (Nothing <: Abstract, after all!).
Generally speaking, in my experience Scala isn't that good even at GADTs, and doing things like that you often have to "help" it by using casts, and it's your responsibility to make sure they are safe.
Lets say I want to write generic function foo that will use pattern matching to check whether passed argument is of type of it's generic argument T
Naive attempt:
def foo[T]: PartialFunction[Any, Boolean] = {
case x: T =>
true
case _ =>
false
}
... won't work since T gets ereased. Compiller warning confirms that:
Warning:(11, 13) abstract type pattern T is unchecked since it is eliminated by erasure
case x: T =>
^
What is the best way to get it work?
Scala has introduced ClassTags for this purpose. They can be obtained by an implicit parameter, and will be automatically provided, which means you don't have to worry about the parameter when calling the method:
import scala.reflect.ClassTag
def foo[T](implicit tag: ClassTag[T]): PartialFunction[Any, Boolean] = {
case x: T =>
true
case _ =>
false
}
val isString = foo[String] // ClassTag gets provided implicitly here
isString("Hallo") // will return true
isString(42) // will return false
For further explanations see the docs.