I have an automatically generated client for an web service. I has many complicated classes and I have to do a pattern matching against it. For now I have a structure looking like this:
val response = client.getResponse
response match {
case Left(_) => None
case Right(a: SomeClass) => a match {
case SomeClass2(b: Option[SomeClass3]) => b match {
case None => None
case Some(c: SomeClass3) => c match {
case SomeClass4(_, _, _, _, d: Seq[SomeClass4]) => d match {
case Nil => None
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
case Nil => None
case SomeClass6(e: SomeClass7) => e match {
case Nil => None
case SomeClass8(f, _, _, _, _) => f match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
case Nil => None
case SomeClass9(g: Seq[SomeClass9], _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
}
}
}
}
}
}
}
}
where SomeClass1 - SomeClass9 are case classes.
As you can see, it seems frightening. What do I do about it? What's the standard way to make it look nicer?
I guess there should be not only refactoring but rather another approach.
Assuming that a should be SomeClass2, but not SomeClass (same with b, c, d).
You could use alternative patterns like case A | B => ... and structural patterns like Some(MyClass(f)).
Also you could use partial function in map like map { case ... } instead of map { x => x match {...} }.
And I guess there is a error in your code: there is check for case Nil => ...; case SomeClass8(...) => ....
You could replace Seq(xs #_*) with xs. If you need entire collection you don't need to extract elements.
Your code:
response match {
case Left(_) | Right(SomeClass2(None)) | Right(SomeClass2(Some(SomeClass3(_, _, _, _, Nil))) => None
case Right(SomeClass2(Some(SomeClass3(_, _, _, _, xs))) =>
xs map {
case SomeClass6(None) | SomeClass6(Some(SomeClass8(Nil, _, _, _, _))) => None
case SomeClass6(Some(SomeClass8(xs, _, _, _, _))) =>
xs map {
case Nil => None
case SomeClass9(g, _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
}
}
}
You should also extract nested matches to separate methods.
Pattern matching is not the only solution. You could use methods of Either and Option:
response.right.toOption.collect {
// No need for the first part.
case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}
You might find extractors useful. It might also be worth flattening some of the cases so you have
case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None
rather than having a None case explicitly defined at each level.
You can probably reduce a lot of this complexity by using for comprehensions rather than pattern matching.
One simple opportunity is where you are mapping a sequence to yet another pattern match:
case seq: Seq[SomeClass5] => seq match {
case Nil => None
case Seq(xs#_*) => xs map { x =>
x match {
...
}
}
}
This is very ugly because you have used match to eliminate the Nil case and then matched seq again. Two levels of match to deal with one object. This could become
case seq: Seq[SomeClass5] => for (x <- seq) yield {
x match {
...
}
}
This eliminates the Nil case check and removes a couple of layers of nesting, which is a big win. And you do this in at least two layers, so that's an even bigger win. Of course, this returns a (possibly Nil) sequence rather than f(x) or None, but you can easily convert that. One way to do this, without adding another of nesting, would be this:
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Nil => None
case Seq(i) => Some(i)
case ...
}
Or, if (as I suspect) you expect these sequences only to have one element in them...
case seq: Seq[SomeClass5] => (for (x <- seq) yield {
x match {
...
}
}) match {
case Seq(i) => Some(i)
case _ => None
}
Related
I have an Option of String which may be empty too. I have written following code to handle different branches of flow:
input match {
case Some(val) => {
val match {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
case _ => extractFromAnotherInput
}
}
None => extractFromAnotherInput
}
private def extractFromAnotherInput = {
anotherInput match {
case a => ....
case b => ....
}
}
Is this a good way of handling code branches in a functional language or it can be done in a better way?
You don't have to nest the matches:
input match {
case Some("sayHi") => "Hi"
case Some("sayHello") => "Hello"
case _ => extractFromAnotherInput
}
You can also combine this input with "another", and "strip" he option before doing the match:
input.getOrElse(anotherInput) match {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
case a => ...
case b => ...
}
I would go with Dima's solution. Or if you want to use some method on Option:
input.collect{
case "sayHi" => "Hi"
case "sayHello" => "Hello"
}
.getOrElse(extractAnotherInput)
Just because, I would like to propose an alternative to Levi's answer; just to remark this is more of a matter of opinion.
input.filter(_.nonEmpty).fold(ifEmpty = extractAnotherInput) {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
}
This is a matter of style: I personally dislike pattern-matching on Option, but that's idiosyncratic (I also may be the only Scala programmer who generally dislikes the for notation, but that's neither here nor there).
I'd express that as
input.flatMap { v =>
v match {
case "sayHi" => Some("Hi")
case "sayHello" => Some("Hello")
case _ => None
}
}.getOrElse(extractAnotherInput)
If overfitting to this example (exploiting the fact that I can uniformly transform sayHi and sayHello)
input.filter(v => v == "sayHi" || v == "sayHello")
.map(_.drop(3))
.getOrElse(extractAnotherInput)
I'm having a def that is recursively called and I'm using a bunch of cases.
I'm wondering is there is any nice solution to get rid of that cases, without losing the readability of the definition.
#tailrec
def getElements(existent:List[List[String]], proposed: List[List[String]], result: List[(String,List[String])]): List[(String, List[String])]= {
proposed match {
case head :: tail => {
existent.find(element => identicalOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Same", elem) :: result)
case None => {
existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), head::tail, ("Delete", elem) :: result)
case None => {
existent.find(element => firstOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)
case None => {
existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)
case None => getElements(Nil, Nil, existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: result)
}
}
}
}
}
}
}
}
case Nil => result
}
}
Your method readability would benefit greatly if you could split it into several methods.
Unfortunately, you can't do that if you want your function to be tail-recursive.
But there is a solution which is called trampoline, which would allow you to create an arbitrarily deep chain of function recursive calls which are stack-safe.
Trampolines are implemented in Scala standard library in package scala.util.control.TailCalls. I reimplemented your method to take advantage of it.
First thing I did was removing an accumulator parameter from getElements, we won't need it anymore.
Then I split getElements into three functions. I called nested functions ifNoneMatched and ifNoneMatched2 but you could probably come up with more meaningful names.
Then I wrapped every call to a function which is in the chain into tailcall and every constant into done (in our case Nil). When I needed to append something to list returned from a recursive call I used flatMap from TailRec.
import scala.util.control.TailCalls._
def getElements(existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]]= {
proposed match {
case head :: tail => {
existent.find(element => identicalOfMatch(element, head)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), tail).map(("Same", elem) :: _))
case None => tailcall(ifNoneMatched(head, tail, existent, proposed))
}
}
case Nil => done(Nil)
}
}
def ifNoneMatched(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), proposed)).map(("Delete", elem) :: _)
case None => tailcall(ifNoneMatched2(head, tail, existent, proposed))
}
}
def ifNoneMatched2(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
existent.find(element => firstOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail).map(("Update", head) :: _)
case None => {
existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), tail)).map(("Update", head) :: _)
case None => getElements(Nil, Nil).map(existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: _)
}
}
}
}
getElements returns now TailRec[List[(String, List[String])]], but you can unwrap it by just calling result.
Of course, you can nest methods even deeper. Until you wrap your method calls into tailcall your stack is safe.
I didn't reimplement your methods like identicalOfMatch etc. so I couldn't really test if my implementation works. If something breaks please let me know ;)
I have an scenario where I need to call up to three services to do something. Each service has some kind of priority and my algorithm depends on the combination of the result of each service (all of them, two or even one). In order to handle this situation I want to use pattern matching (because the matchs, and variable extraction)
Here you are a simplified example.
case class Foo(bar: String, baz: Option[String])
def expensiveOperation1(): String = ???
def expensiveOperation2(): List[Int] = ???
def expensiveOperation3(): Foo = ???
lazy val r1 = expensiveOperation1()
lazy val r2 = expensiveOperation2()
lazy val r3 = expensiveOperation3()
(r1, r2, r3) match {
case ("Value1", _, _) => "1"
case ("Value2", _, _) => "2"
case (_, List(1), _) => "3"
case (_, Nil, _) => "4"
case ("Value3", 1 :: tail, _) => "5" + tail
case (_, _, Foo("x", Some(x))) => x
case (_, _, _) => "7"
}
As you can see, there is no need to call expensiveOperation2 and expensiveOperation3 all the time, but though I save each result on lazy vals, in the moment when I create Tuple3, each method is called.
I could create a container LazyTuple3 with three params call by name in order to solve that problem, but I'll get a new problem, unapply method (LazyTuple3.unapply) returns an Option, so after the first "case" each method will be called.
I could solve this with nested "if" or "match" but I want to give a chance with one "match", I find it clearer.
Any idea?
Thanks in advance.
Try to use scalaz.Need. https://static.javadoc.io/org.scalaz/scalaz_2.12/7.2.26/scalaz/Need.html
case class Foo(bar: String, baz: Option[String])
def expensiveOperation1(): String = {
println("operation1")
"Value3"
}
def expensiveOperation2(): List[Int] = {
println("operation2")
List(1, 2, 3)
}
def expensiveOperation3(): Foo = {
println("operation3")
Foo("x", Some("x"))
}
lazy val r1 = Need(expensiveOperation1())
lazy val r2 = Need(expensiveOperation2())
lazy val r3 = Need(expensiveOperation3())
(r1, r2, r3) match {
case (Need("Value1"), _, _) => "1"
case (Need("Value2"), _, _) => "2"
case (_, Need(List(1)), _) => "3"
case (_, Need(Nil), _) => "4"
case (Need("Value3"), Need(1 :: tail), _) => "5" + tail
case (_, _, Need(Foo("x", Some(x)))) => x
case (_, _, _) => "7"
}
This will print:
operation1
operation2
Is there an easy/best way to get a BitSet I can pattern match like a list?
val btst = BitSet(1,2,3,4)
btst match {
...
case head :: tail => tail
}
By definition a set is an unordered collection, and pattern matching over such one is error-prone. Convert it to list if you want to... Also, you should not rely on head and tail to always return the same thing.
A BitSet is ordered, but extractorless.
Edit: but not humorless.
object |<| {
def unapply(s: BitSet): Option[(Int, BitSet)] =
if (s.isEmpty) None
else Some((s.head, s.tail))
}
def flags(b: BitSet) = b match {
case f"5 || 10" => println("Five and dime") // alas, never a literal
case 5 |<| any => println(s"Low bit is 5iver, rest are $any")
case i |<| any => println(s"Low bit is $i, rest are $any")
case _ => println("None")
}
def dump(b: BitSet) = println(b.toBitMask.mkString(","))
val s = BitSet(5, 7, 11, 17, 19, 65)
dump(s)
// ordinary laborious tests
s match {
case x if x == BitSet(5) => println("Five")
case x if x == BitSet(5,7,11,17,19,65) => println("All")
case x if x(5) => println("Five or more")
case _ => println("None")
}
// manually matching on the mask is laborious
// and depends on the bit length
s.toBitMask match {
case Array(2L) => println("One")
case Array(657568L) => println("First word's worth")
case Array(657568L, _) => println("All")
case _ => println("None")
}
// or truncate for special case
s.toBitMask(0) match {
case 2L => println("One")
case 657568L => println("First word's worth")
case _ => println("None")
}
How does one combine (in a nice way) two Scala match'es?
First I have to test if an Option is a valid value:
myOption match {
case Some(op) =>
doSomethingWith(op)
case None =>
handleTheError()
Then if op was valid, I want to test for another pattern:
Path(request.path) match {
case "work" => {
println("--Let's work--")
}
case "holiday" => {
println("--Let's relax--")
}
case _ => {
println("--Let's drink--")
}
}
I could combine them like this:
myOption match {
case Some(op) =>
doSomethingWith(op)
Path(request.path) match {
case "work" => {
println("--Let's work--")
}
case "holiday" => {
println("--Let's relax--")
}
case _ => {
println("--Let's drink--")
}
}
case None =>
handleTheError()
But, it feels sloppy. Is there a better way to combine them in some way or another.
Update
Apologies, I should have explained better. I'm actually trying to find out if there is a known pattern for simplifying (or factoring out) these control structures. For example (imagine this was true):
x match {
case a => {
y match {
case c => {}
case d => {}
}
}
case b => {}
}
equals
x -> y match {
case a -> c => {}
case a -> d => {}
case b => {}
}
I was just wandering if someone has already identified some refactoring patterns for control flow, much like algebra where 2(x + y) = 2x + 2y
You can do
myOption map { success } getOrElse handleTheError
or with scalaz,
myOption.cata(success, handleTheError)
where success is something like
def success(op: Whatever) = {
doSomethingWith(op)
Path(request.path) match {
case "work" => println("--Let's work--")
case "holiday" => println("--Let's relax--")
case _ => println("--Let's drink--")
}
}
Update
Your pseudocode
x -> y match {
case a -> c => {}
case a -> d => {}
case b => {}
}
can be literally translated to scala as
(x, y) match {
case (a, c) => {}
case (a, d) => {}
case (b, _) => {}
}
It looks nice (and that's probably what you wanted) if inner matcher have only few options (c and d in this case), but it leads to code duplication (repeating of pattern a). So, in general I'd prefer map {} getOrElse {}, or separation of pattern-matchers on smaller functions. But I repeat, in your case it looks reasonable.
If the scrutinees (the expressions on which you match) do not depend on each other, i.e. you can compute them independently, you could perform both matches at the same time by wrapping the scrutinees in a tuple.
So your example:
x match {
case a => {
y match {
case c => fooC(...)
case d => fooD(...)
}
}
case b => fooB(...)
}
could be rewritten as:
(x,y) match {
case (a, c) => fooC(...)
case (a, d) => fooD(...)
case (b, _) => fooB(...)
}
...as long as y doesn't depend on x.