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)
}
}
Related
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) => ...
}
Here is the code
class Result[A] {
def map[B](f: (Result[A] => Result[B]), xResult: Result[A]) = {
xResult match {
case Success(x) => Success(f(xResult))
case Failure(errs) => Failure(errs)
}
}
def apply[B](fResult: Result[A], xResult: Result[B]) = {
(fResult, xResult) match {
case (Success(f), Success(x)) => Success((f, x))
case (Failure(errs), Success(a)) => Failure(errs)
case (Success(a), Failure(errs)) => Failure(errs)
case (Failure(errs), Failure(errs2)) => Failure(List(errs, errs2))
}
}
}
case class Success[A](a: A) extends Result[A] {}
case class Failure[A](a: A) extends Result[A] {}
def createCustomerId(id: Int) = {
if (id > 0)
Success(id)
else
Failure("CustomerId must be positive")
}
Here are the questions
1) result from type inference from method createCustomer is like this
Product with Serializable with Result[_ >: Int with String]
I dont have trouble with the "Product with Serializable" stuff, the thing im wondering is how to do something meaningfull with the result, and just out of curiosity how to initialize a type like that.
suppose i have another method as shown below
def createCustomer(customerId: Result[_ >: Int with String], email: Result[String]) = {
how can i read/do something with "customerId" argument
}
how to initialize a type like that
val x: Result[Int with String] = ???
notes:
i know my Result class doesnt have any generic "getter"
I know that perhaps things could be easier in method "createCustomerId" if when id > 0 i returned Success(id.ToString), but i really want to know whats going on with this lower type and how i could take advantage (if possible) in the future
The problem is that your type definition states that both Success and Failure take the same argument type. However, you actually want to put some other type of argument into Failure while still keeping covariance. Here is the simplest fix:
class Result[A] {
def map[B](f: (Result[A] => Result[B]), xResult: Result[A]) = {
xResult match {
case Success(x) => Success(f(xResult))
case Failure(errs) => Failure(errs)
}
}
def apply[B](fResult: Result[A], xResult: Result[B]) = {
(fResult, xResult) match {
case (Success(f), Success(x)) => Success((f, x))
case (Failure(errs), Success(a)) => Failure(errs)
case (Success(a), Failure(errs)) => Failure(errs)
case (Failure(errs), Failure(errs2)) => Failure(List(errs, errs2))
}
}
}
case class Success[A](a: A) extends Result[A] {}
case class Failure[A, B](a: B) extends Result[A] {}
object TestResult extends App {
def createCustomerId(id: Int): Result[Int] = {
if (id > 0)
Success(id)
else
Failure("CustomerId must be positive")
}
println(createCustomerId(0))
println(createCustomerId(1))
}
prints:
Failure(CustomerId must be positive)
Success(1)
If you don't annotate return type for createCustomerId with Result[Int] compiler will infer Result[_ <: Int] with Product with Serializable which is probably not ideal but not too bad.
Can a Scala case object be used in a match case?
E.g. this does not work:
abstract class A
case object B extends A
object something {
val b = B
b match { case _:B => println("success") }
}
not found: type B
b match { case _:B => println("success") }
^
You need to specify B.type:
object something {
val b = B
b match { case _:B.type => println("success") }
}
Oops, seems that this also compiles:
abstract class A
case object B extends A
object something {
val b = B
b match { case B => println("success") }
}
Scala Fiddle: Can a Scala case object be used in a match case
Assuming you have the following code
trait T {
}
case class First(int:Int) extends T
case class Second(int:Int) extends T
val a:Option[T] = Option(First(3))
val b:Option[Second] = None
def test[A](a:Option[A])(implicit manifest:Manifest[Option[A]]) = {
a match {
case Some(z) => println("we have a value here")
case None => a match {
case t:Option[First] => println("instance of first class")
case s:Option[Second] => println("instance of second class")
}
}
}
test(b)
How would you extract what type the enclosing A is if the option happens to be a None. I have attempted to do various combinations of manifests, but none of them seem to work, each time it complains about the types being eliminated with erasure, i.e.
non-variable type argument First in type pattern Option[First] is unchecked since it is eliminated by erasure
You can use a type tag, a modern replacement for Manifest:
import scala.reflect.runtime.universe._
trait T
case class First(int:Int) extends T
case class Second(int:Int) extends T
def test[A: TypeTag](a: Option[A]) = {
a match {
case Some(_) => println("we have a value here")
case None => typeOf[A] match {
case t if t =:= typeOf[First] => println("instance of First")
case t if t =:= typeOf[Second] => println("instance of Second")
case t => println(s"unexpected type $t")
}
}
}
Example
val a = Option(First(3)) // we have a value here
val b: Option[First] = None // instance of First
val c: Option[Second] = None // instance of Second
val d: Option[Int] = None // unexpected type Int
By the way, you are interested in the type of A (which is being eliminated by erasure), so even with manifests you need one on A and not on Option[A].
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)