I have a case class like this:
case class AEMError(
authError: Option[AEMAuthError] = None,
unexpectedError: Option[AEMUnexpectedError] = None,
)
I want to do a pattern matching like this:
def throwErrorAndLogMessage(error:AEMError,message:String):Unit={
error match {
case authError: Some[AEMAuthError] => {???}
case unexpectedError: Some[AEMUnexpectedError] => {???}
case _ => {???}
}
}
and add multiple case classes for different types of error but I am totally spinning on the syntax for case match can somebody help me here?
With your definition of AEMError you can't do reasonable pattern matching because it possible to have both error present.
You should change AEMError to
sealed trait AEMError
case class AEMErrorAuth(authError: AEMAuthError) extends AEMError
case class AEMErrorUnexpected(authError: AEMUnexpectedError) extends AEMError
And use it like this:
err match {
case AEMErrorAuth(authError) => {
print(authError)
}
case AEMErrorUnexpected(unexpectedError) => {
print(unexpectedError)
}
}
If you can't change the signature of AEMError like #talex mentioned, you can try something like:
def throwErrorAndLogMessage(error: AEMError, message: String): Unit = {
error match {
case AEMError(Some(authError), Some(unexpectedError)) => ???
case AEMError(Some(authError), None) => ???
case AEMError(None, Some(unexpectedError)) => ???
case _ => ???
}
}
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)
}
}
I have enum like this.
object SortCountryField extends Enumeration {
type SortCountryField = Value
val countryName = Value("country_name")
val countryStatus = Value("country_status")
}
I'm using this SortCountryField enum in match-case.
Here I need to convert toString every time.
To make it convenient I'm trying to implicit converter to extract String from SortCountryField.{Value}
However, I end up having a compiler error when I use the implicit function in following match case.
'myString' match{
case SortCountryField.countryName.toString => //Some operations
case SortCountryField.countryStatus.toString => //another operation
}
Error Log:-
found : mypackage.ConstantUtils.SortCountryField.Value
[error] required: String
[error] case SortCountryField.countryStatus => //my-operations
I think you'd be better off using the enum in your match like:
SortCountryField withName <your_string> match {
case SortCountryField.countryName => //Some operations
case SortCountryField.countryStatus => //another operation
}
If your string sometimes doesn't match any field then you can easily wrap this in a Try like in the following code:
Try(SortCountryField withName <your_string>) match {
case Success(SortCountryField.countryName) => //Some operations
case Success(SortCountryField.countryStatus) => //another operation
case _ => //another operation
}
You can also do:
'myString' match{
case x if x == SortCountryField.countryName.toString => //Some operations
case x if x == SortCountryField.countryStatus.toString => //another operation
}
How does your implicit converter looks like?
My guess is that you have converter SortCountryField => String, but you need SortCountryField.Value => String converter.
Add below function in your enum:
implicit def toString(value: Value): String = value.toString
Use below in matching:
val countryStaus:String = SortCountryField.countryStatus
'myString' match {
case `countryStatus` => //Some operations
case _ => // Another operation
}
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) => ...
}
Having a List[String] and several parsers I want to pattern match each String from the List to parsers. So it'd look like this (Warning, pseudo-code):
myStringList.map{
case MyParser.keyword => keyword match {
case KeywordParser.keyword1 => //it's special keyword1
case KeywordParser.keyword2 => //special treatment for keyword2
case NotSpecial => //it's a usual command
}
case MyParser.stringValue => //etc...
}
Why would I want to do so?
I'm parsing a simple script line, that contains "strings" and $(keywords). Some of the keywords are special and need to be treated separately.
Currently I have only one special keyword, so I'm using chained parseAll and match, but that doesn't feel right.
So, how it can be done?
It's not really supported, but you can do it like this:
val parseKeyword: PartialFunction[String, ... ] {
case KeywordParser.keyword1 => //it's special keyword1
case KeywordParser.keyword2 => //special treatment for keyword2
case NotSpecial => //it's a usual command
}
myStringList.map{
case x#MyParser.keyword if parseKeyword.isDefinedAt(x) => parseKeyword(x)
case MyParser.stringValue => //etc...
}
Bet I had a short-circuit in my brain, because I woke up next morning and realised, that all I need is a couple of parsers, some case classes and a pair of pattern matchers. So the solution to my problem looks like this (pseudo-code):
case class SpecialKeyword1(value: String)
case class SpecialKeyword2(value: String)
...
case class SpecialKeywordN(value: String)
case class SimpleKeyword(value: String)
object MyParser extends JavaTokenParsers{
def expression: Parser[Any] = ...
def keyword1: Parser[SpecialKeyword1] = ...
def keyword2: ...
...
def simplekeyword: Parser[SimpleKeyword] = ...
}
object Main {
def parseScript(inputScript: String): String = {
MyParser.parseAll(MyParser.expression, inputScript) match {
case Success(result, _) => {
case SpecialKeyword1(val) => ...
case SpecialKeyword2(val) => ...
...
case SimpleKeyword(val) => ...
case str: String => ...
}
case Failure(_, _) => "Parsing Failure"
case Error(_, _) => "Parsing Error"
}
}
}
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)