Say I have something like this:
obj match {
case objTypeOne : TypeOne => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
Now I want to generalise, to pass in one of the types to match:
obj match {
case objTypeOne : clazz => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
But this isn't allowed, I think for syntactic rather than semantic reasons (although I guess also that even though the clazz is a Class[C] the type is erased and so the type of the Option will be lost).
I ended up with:
if(clazzOne.isAssignableFrom(obj.getClass)) Some(clazz.cast(obj))
if(obj.isInstanceOf[TypeTwo]) Some(obj.asInstanceOf[TypeTwo])
None
I just wondered if there was a nicer way.
You could define an extractor to match your object:
class IsClass[T: Manifest] {
def unapply(any: Any): Option[T] = {
if (implicitly[Manifest[T]].erasure.isInstance(any)) {
Some(any.asInstanceOf[T])
} else {
None
}
}
}
So let's test it:
class Base { def baseMethod = () }
class Derived extends Base
val IsBase = new IsClass[Base]
def test(a:Any) = a match {
case IsBase(b) =>
println("base")
b.baseMethod
case _ => println("?")
}
test(new Base)
test(1)
You will have to define a val for your extractor, you can't inline IsBase, for example. Otherwise it would be interpreted as an extractor.
You could use pattern guards to achieve that. Try something like this:
obj match {
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case objTypeOne if clazz.isAssignableFrom(objTypeOne.getClass) => Some(clazz.cast(objTypeOne))
case _ => None
}
You can use a local type alias for that:
def matcher[T](obj: Any)(implicit man: Manifest[T]) = {
val instance = man.erasure.newInstance.asInstanceOf[AnyRef]
type T = instance.type // type alias
obj match {
case objTypeOne : T => "a"
case objTypeTwo : TypeTwo => "b"
case _ => "c"
}
}
scala> matcher[TypeOne](TypeOne())
res108: java.lang.String = a
scala> matcher[TypeTwo](TypeOne())
res109: java.lang.String = c
UPDATE: Aaron Novstrup has pointed out that singleton type will only work if man.erasure.newInstance==obj (see ยง3.2.1 of the spec)
Related
I have a class which contains a sequence of a generic type like:
sealed trait Interface {}
case class Imp1() extends Interface {}
case class Imp2() extends Interface {}
case class Wrapper[+I <: Interface](interface: I) {}
case class WrapperList(list: Seq[Wrapper[Interface]]) {
...
}
In the WrapperList I want to be able to iterate through the Sequence of wrappers and pattern match on each ones type, eg.
def getImp1s(remaining: Seq[Wrapper[Interface]] = list): Seq[Wrapper[Imp1]] = {
if (remaining.length == 0) Seq()
else remaining.head match {
case wrapper: Wrapper[Imp1] => get(remaining.tail) :+ wrapper
case _ => get(remaining.tail)
}
}
As you can probably guess I'm running into
non-variable type argument Playground.Imp1 in type pattern Playground.Wrapper[Playground.Imp1] is unchecked since it is eliminated by erasure
To overcome this I was under the impression I could use TypeTags or ClassTags to preserve the type, something like:
case class Wrapper[+I <: Interface](interface: I)(implicit tag: TypeTag[I]) {}
However this doesn't seem to work, I still get the same warning. Could someone explain how I can match using the TypeTag? I'd rather avoid creating concrete versions of my generic class which extend a generic abstract class, but do understand that this is maybe the easiest solution.
Thanks for your help :)
Try
import shapeless.TypeCase
val `Wrapper[Imp1]` = TypeCase[Wrapper[Imp1]]
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case `Wrapper[Imp1]`(wrapper) => getImp1s(remaining.tail) :+ wrapper
case _ => getImp1s(remaining.tail)
}
}
getImp1s(Seq(Wrapper(Imp1()), Wrapper(Imp2()), Wrapper(new Interface {})))
// List(Wrapper(Imp1()))
getImp1s(Seq(Wrapper(Imp2()), Wrapper(Imp1()), Wrapper(new Interface {})))
// List(Wrapper(Imp1()))
The same can be achieved without Shapeless with custom extractor
object `Wrapper[Imp1]` {
def unapply(arg: Any): Option[Wrapper[Imp1]] = arg match {
case Wrapper(Imp1()) => Some(Wrapper(Imp1()))
case _ => None
}
}
or directly
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case Wrapper(Imp1()) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
case _ => getImp1s(remaining.tail)
}
}
or
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case Wrapper(_: Imp1) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
case _ => getImp1s(remaining.tail)
}
}
import scala.reflect.runtime.universe._
val a: 42 = 42
val t: Type = typeOf[a.type]
assert(getConstantType(t).get =:= typeOf[42])
def getConstantType(t: Type): Option[ConstantType] = ???
How could I generally implement getConstantType so that the above assertion passes?
I assumed that something like this was possible since the assertion below passes:
assert(t <:< typeOf[42])
t.widen goes too far as it return Int. I'm looking for something that returns Int(42).
How about
assert(t.resultType =:= typeOf[42])
Updated -
def getConstantType[T](t: T): t.type = t
Update 2 -
def getConstantType(tp: Type): Option[ConstantType] = {
tp.erasure match {
case ConstantType(_) => Some(tp.erasure.asInstanceOf[ConstantType])
case _ => None
}
}
Try
def getConstantType(tp: Type): Option[ConstantType] = {
def unrefine(t: Type): Type = t.dealias match {
case RefinedType(List(t), scope) if scope.isEmpty => unrefine(t)
case t => t
}
unrefine(tp) match {
case SingleType(_, sym) => sym.typeSignature match {
case NullaryMethodType(t) => unrefine(t) match {
case c: ConstantType => Some(c)
case _ => None
}
case _ => None
}
case _ => None
}
}
I have a type whose shape is like this:
val myType: Future[Either[MyError, TypeA]] = // some value
I know that I could pattern match on this and get to the Right or Left type, but the problem is that I would have to nest my pattern matching logic. I'm looking for much more elegant way of handling this? Any suggestions?
If you encode your MyError as an exception, you don't need the Either anymore and can simply patternMatch against the completion, or use a recoverWith to map it to another type:
myType.onComplete {
case Success(t) =>
case Failure(e) =>
}
To map your existing Either types you could do something like this:
case class MyException(e: MyError) extends Exception
def eitherToException[A](f: Future[Either[MyError,A]]): Future[A] = {
f.flatMap {
case Left(e) => Future.failed(MyException(e))
case Right(x) => Future.successful(x)
}
}
val myType2 = eitherToException(myType)
Alternatively, if MyError and TypeA are under your control, you could create a common super type and pattern match against that:
sealed trait MyResult
final case class MyError() extends MyResult
final case class TypeA() extends MyResult
myType.map {
case MyError() => ...
case TypeA() => ...
}
You can create custom extractor objects:
object FullSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Right(x)) => Some(x)
case _ => None
}
}
object PartSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Left(err)) => Some(err)
case _ => None
}
}
And
val myType: Future[Either[MyError, TypeA]] = // some value
myType.onComplete {
case FullSuccess(x) => ... // equivalent to case Success(Right(x))
case PartSuccess(x) => ... // equivalent to case Success(Left(x))
case Failure(e) => ...
}
I want to check the type of the parameters of a method, but I don't know the best way to do this. See my code:
class X {
def x(a: Int, b: String) {}
}
val methods = classOf[X].getDeclaredMethods
methods map { m =>
m.getParameterTypes.toList map { t =>
println(t.getName)
// I don't know how to write the following
if ( the type of t is Int) { do something}
else if( the type of t is String ) { do something}
else { }
}
}
Please note the comment in the code. I don't know how to check the types in scala way.
I've tried:
t match {
case _:String => println("### is a string")
case _:Int => println("### is an int")
case _ => println("### ?")
}
But it can't be compiled.
I can use java-way to check:
if (t.isAssignableFrom(classOf[String])) // do something
else if(t.isAssignableFrom(classOf[Int])) // do something
else {}
It seems we should use it in scala, right?
UPDATE:
If I want to use match, I should write like this:
t match {
case i if i.isAssignableFrom(classOf[Int]) => println("### is an Int")
case s if s.isAssignableFrom(classOf[String]) => println("### is a String")
case _ => println("###?")
}
Is it the best answer?
I could make it work with t as a type by defining the cases as constants. It wouldn't compile with the class literals as the case expression. Try:
val S = classOf[String]
val I = classOf[Int]
t match {
case S => println("### is a string")
case I => println("### is an int")
case _ => println("### ?")
}
You can use ClassManifest.fromClass to correctly handle the coercion of primitives to AnyVals, and any other such troubles you might have encountering boxed vs unboxed types when getting funky with reflection.
Like this:
import reflect.ClassManifest
class Wibble { def method(a:Int, b: String) = () }
for(method <- classOf[Wibble].getDeclaredMethods; paramType <- method.getParameterTypes) {
ClassManifest.fromClass(paramType) match {
case m if m <:< ClassManifest.Int => println("Interiffic")
case m if m <:< ClassManifest.Float => println("Floaty, like butterflies")
case m if m <:< ClassManifest.Double => println("Or Quits...")
//todo: all the other AnyVal types...
case m if m <:< classManifest[String] => println("bleeding edge physics, yeah baby!")
//...and a default condition
}
}
I'm doing matching against some case classes and would like to handle two of the cases in the same way. Something like this:
abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(sb) | C(sc) => "B"
case _ => "default"
}
}
But when I do this I get the error:
(fragment of test.scala):10: error: illegal variable in pattern alternative
case B(sb) | C(sc) => "B"
I can get it working of I remove the parameters from the definition of B and C but how can I match with the params?
Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(_) | C(_) => "B"
case _ => "default"
}
}
If you must, must, must extract the parameter and treat them in the same code block, you could:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case bOrC # (B(_) | C(_)) => {
val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
"B(" + s + ")"
}
case _ => "default"
}
}
Though I feel it would be much cleaner to factor that out into a method:
def doB(s: String) = { "B(" + s + ")" }
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(s) => doB(s)
case C(s) => doB(s)
case _ => "default"
}
}
There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.
object MuliCase {
abstract class Foo
case object A extends Foo
trait SupportsS {val s: String}
type Stype = Foo {val s: String}
case class B(s:String) extends Foo
case class C(s:String) extends Foo
case class D(s:String) extends Foo with SupportsS
case class E(s:String) extends Foo with SupportsS
def matcher1(l: Foo): String = {
l match {
case A => "A"
case s: Stype => println(s.s); "B"
case _ => "default"
}
}
def matcher2(l: Foo): String = {
l match {
case A => "A"
case s: SupportsS => println(s.s); "B"
case _ => "default"
}
}
def main(args: Array[String]) {
val a = A
val b = B("B's s value")
val c = C("C's s value")
println(matcher1(a))
println(matcher1(b))
println(matcher1(c))
val d = D("D's s value")
val e = E("E's s value")
println(matcher2(d))
println(matcher2(e))
}
}
The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.
Well, it doesn't really make sense, does it? B and C are mutually exclusive, so either sb or sc get bound, but you don't know which, so you'd need further selection logic to decide which to use (given that they were bound to a Option[String], not a String). So there's nothing gained over this:
l match {
case A() => "A"
case B(sb) => "B(" + sb + ")"
case C(sc) => "C(" + sc + ")"
case _ => "default"
}
Or this:
l match {
case A() => "A"
case _: B => "B"
case _: C => "C"
case _ => "default"
}