Abstract away complexity of complex pattern match in scala - scala

If I'm doing lots of pattern matching against a (relatively) complex case class, but most of the time I'm only interested in one or two of its fields. Is there a way to abstract away the other fields (perhaps by wrapping the class?)? Here's an example of the type of thing I'm trying to simplify:
def receive = {
case HttpRequest(POST, "foo", _, HttpBody(_, body), _) => // action
case HttpRequest(GET, "bar", _, _, _) => // action
}
I'm only ever really interested in the request type, url and sometimes body so I would ideally like to define a pattern match as case Request(POST, "foo", body) or similar.

Just make your own Request extractor. Here's a simplified example:
case class Complex(a: String, b: Int, c: String)
object Simple {
def unapply(c: Complex): Option[(String, Int)] = Some(c.a, c.b)
}
Complex("B", 2, "x") match {
case Simple("A", i) => println("found A, " + i)
case Simple("B", i) => println("found B, " + i)
}
// prints "found B, 2"

Related

Scala optimizing optional string parsing

We need to create an request query-string, from a case class. The case class contains optional attributes:
case class Example(..., str: Option[String], ..)
We want to create a query-parameter if the option exists, and no query parameter otherwise. Like:
match example.str {
case Some(s) => s"&param_str=$s"
case _ => ""
}
as this appearing at numerous places I want it to make a bit more generic:
def option2String(optionString: Option[String], template: String) = {
optionString match {
case Some(str) => template.replaceAll("\\$str", str)
case _ => ""
}
But I think it can be done more elegant or scala idiomatic, may be with call-by-name arguments?
I would use fold
example.str.fold("")("&param_str=" + _)
If you have multiple parameters you can try this:
List(
str1.map("&param1_str=" + _),
str2.map("&param2_str=" + _),
str3.map("&param3_str=" + _)
).flatten.mkString(" ")

How to explain these pattern matching examples?

I wrote some events in FSM, and discovered something I can not explain when pattern matching. I thought the following was completely legal, that is that I can send this actor either a message which is a vector[A] or vector[B].
when(State) {
case Event(content: Vector[A], _) => {
println("matched A")
stay
}
case Event(content: Vector[B], _) => {
println("matched B")
stay
}
}
However,
if I send the actor a vector[B] message it leads to
java.lang.ClassCastException: B cannot be cast to A
So basically it tries to match the first event eventhough the next would match.
I tried to make an even simpler pattern match example;
object Pattern extends App {
val n = Vector(1,2,3)
val s = Vector("S", "S", "S")
n match{
case e:Vector[String] => println("matched string")
case v:Vector[Int] => println("matched int")
}
}
This is actually not legal;
Error:(8, 12) pattern type is incompatible with expected type;
found : Vector[String]
required: scala.collection.immutable.Vector[Int]
case e:Vector[String] => println("matched string")
However, I am allowed to run my code if I do the following cast;
object Pattern extends App {
val n = Vector(1,2,3).asInstanceOf[Vector[Any]]
val s = Vector("S", "S", "S")
n match{
case e:Vector[String] => println(n(0).getClass)
case v:Vector[Int] => println("matched int")
}
}
The thing I find strange then is that I apparently say that Any could match a String, but the print is java.lang.Integer. So should I think of it as I have an vector[Int] that I say is a Vector[Any], since Vector[Any] could be a Vector[String] it matches that pattern, and again since it really is a vector[Int] I mask as Vector[Any] the print is fine too.
Could someone explain these pattern matching observations?
and how should I set up the messages so my state can handle both messages of Vector[A] and Vector[B]?
Due to type erasure of jvm type information is lost at runtime this kind of pattern matching (pattern matching with higher kinded types) is not supported directly.
Here are the ways to get around this problem
Instead I recommend you to wrap the vector in another container.
sealed trait Vectors
case class VectorString(vs: Vector[String]) extends Vectors
case class VectorInt(vi: Vector[Int]) extends Vectors
def doStuff(v: Vectors) = v match {
case VectorString(vs) => //be sure that vs is Vector[String]
case VectorInt(vi) =>
}
Ways to pattern match generic types in Scala
Using TypeTag
import scala.reflect.runtime.universe._
def handle[A: TypeTag](a: A): Unit =
typeOf[A] match {
case t if t =:= typeOf[List[String]] =>
// list is a string list
val r = a.asInstanceOf[List[String]].map(_.length).sum
println("strings: " + r)
case t if t =:= typeOf[List[Int]] =>
// list is an int list
val r = a.asInstanceOf[List[Int]].sum
println("ints: " + r)
case _ => // ignore rest
}
val ints: List[Int] = Nil
handle(List("hello", "world")) // output: "strings: 10"
handle(List(1, 2, 3)) // output: "ints: 6"
handle(ints) // output: "ints: 0" it works!

scala coding to do the Huffman decoding, but wrong result

abstract class CodeTree
case class Fork(left: CodeTree, right: CodeTree, chars: List[Char], weight: Int) extends CodeTree
case class Leaf(char: Char, weight: Int) extends CodeTree
type Bit = Int
def decode(tree: CodeTree, bits: List[Bit]): List[Char] = {
if(!bits.isEmpty) {
bits.head match {
case 0 => tree match {
case Fork(l, r, _, _) => decode(l, bits.tail)
case Leaf(_, _) => chars(tree) ::: decode(frenchCode, bits.tail)
}
case 1 => tree match {
case Fork(l, r, _, _) => decode(r, bits.tail)
case Leaf(_, _) => chars(tree) ::: decode(frenchCode, bits.tail)
}
}
}
else Nil
}
val frenchCode: CodeTree = Fork(Fork(Fork(Leaf('s',121895),Fork(Leaf('d',56269),Fork(Fork(Fork(Leaf('x',5928),Leaf('j',8351),List('x','j'),14279),Leaf('f',16351),List('x','j','f'),30630),Fork(Fork(Fork(Fork(Leaf('z',2093),Fork(Leaf('k',745),Leaf('w',1747),List('k','w'),2492),List('z','k','w'),4585),Leaf('y',4725),List('z','k','w','y'),9310),Leaf('h',11298),List('z','k','w','y','h'),20608),Leaf('q',20889),List('z','k','w','y','h','q'),41497),List('x','j','f','z','k','w','y','h','q'),72127),List('d','x','j','f','z','k','w','y','h','q'),128396),List('s','d','x','j','f','z','k','w','y','h','q'),250291),Fork(Fork(Leaf('o',82762),Leaf('l',83668),List('o','l'),166430),Fork(Fork(Leaf('m',45521),Leaf('p',46335),List('m','p'),91856),Leaf('u',96785),List('m','p','u'),188641),List('o','l','m','p','u'),355071),List('s','d','x','j','f','z','k','w','y','h','q','o','l','m','p','u'),605362),Fork(Fork(Fork(Leaf('r',100500),Fork(Leaf('c',50003),Fork(Leaf('v',24975),Fork(Leaf('g',13288),Leaf('b',13822),List('g','b'),27110),List('v','g','b'),52085),List('c','v','g','b'),102088),List('r','c','v','g','b'),202588),Fork(Leaf('n',108812),Leaf('t',111103),List('n','t'),219915),List('r','c','v','g','b','n','t'),422503),Fork(Leaf('e',225947),Fork(Leaf('i',115465),Leaf('a',117110),List('i','a'),232575),List('e','i','a'),458522),List('r','c','v','g','b','n','t','e','i','a'),881025),List('s','d','x','j','f','z','k','w','y','h','q','o','l','m','p','u','r','c','v','g','b','n','t','e','i','a'),1486387)
val secret: List[Bit] = List(0,0,1,1,1,0,1,0,1,1,1,0,0,1,1,0,1,0,0,1,1,0,1,0,1,1,0,0,1,1,1,1,1,0,1,0,1,1,0,0,0,0,1,0,1,1,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1)
def decodedSecret: List[Char] = decode(frenchCode, secret)ode here
I am new to scala, and learning the pattern matching now, I want to do the huffman decoding, now I could get a list, but it is the wrong answer, hope someone could find the mistake.
There are several issues with your code.
You do not want to consume a bit when you hit a leaf. A character of the leaf should also be added if there is no bit in your code.
In the decode methode you do not want to reference frenchCode, but code instead that is given as a parameter.
You can access the char of the leaf via pattern matching, i.e. case Leaf(codeChar, _) => ...
Btw. your code will be way cleaner if you start matching on the tree. Only if it matches to a fork you look at the head of your bit list.
Hope that helps. ;)

re-use a guard in Scala

I often find myself wanting to reuse the result of a guard evaluation in scala, e.g.
blah match {
case Blah(a, b) if expensive(a) < 10 =>
expensive(a)
case _ => b
}
Is this possible using some lesser-known incantation? (putting an # on the expensive doesn't work)
Will this be possible anytime soon?
You can do something similar using a custom extractor. This should work:
case class Blah(a: Int, b: Int)
object expensive {
def unapply(x: Int): Option[Double] = Some(math.cos(x))
}
Blah(1, 1) match {
case Blah(a # expensive(e), b) if e < 10 => println(a, b, e)
case _ => println("nothing")
}
Be sure that the expensive is really more expensive that creating an Option object, which is what the above does.

How to pattern match large Scala case classes?

Consider the following Scala case class:
case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)
Pattern matching allows me to extract one field and discard others, like so:
someVal match {
case WideLoad(_, _, _, d, _) => d ! SomeMessage(...)
}
What I would like to do, and what's more relevant when a case class has ~20 odd fields, is to extract only a few values in a way that does not involve typing out WideLoad(_, _, _, _, _, some, _, _, _, thing, _, _, interesting).
I was hoping that named args could help here, although the following syntax doesn't work:
someVal match {
case WideLoad(d = dActor) => dActor ! SomeMessage(...)
// ^---------- does not compile
}
Is there any hope here, or am I stuck typing out many, many _, _, _, _?
EDIT: I understand that I can do case wl # WideLoad(...whatever...) => wl.d, yet I'm still wondering whether there's even terser syntax that does what I need without having to introduce an extra val.
I don't know if this is appropriate, but you can also build an object just to match that field, or that set of fields (untested code):
object WideLoadActorRef {
def unapply(wl: WideLoad): Option[ActorRef] = { Some(wl.d) }
}
someVal match {
case WideLoadActorRef(d) => d ! someMessage
}
or even
object WideLoadBnD {
def unapplySeq(wl: WideLoad): Option[(Int,ActorRef)] = { Some((wl.b,wl.d)) }
}
someVal match {
case WideLoadBnD(b, d) => d ! SomeMessage(b)
}
You can always fall back to guards. It's not really nice, but better than normal pattern matching for you monster case classes :-P
case class Foo(a:Int, b:Int, c:String, d:java.util.Date)
def f(foo:Foo) = foo match {
case fo:Foo if fo.c == "X" => println("found")
case _ => println("arrgh!")
}
f(Foo(1,2,"C",new java.util.Date())) //--> arrgh!
f(Foo(1,2,"X",new java.util.Date())) //--> found
That said I think you should rethink your design. Probably you can logically group some parameters together using case classes, tuples, lists, sets or maps. Scala does support nested pattern matching:
case class Bar(a: Int, b:String)
case class Baz(c:java.util.Date, d:String)
case class Foo(bar:Bar, baz:Baz)
def f(foo:Foo) = foo match {
case Foo(Bar(1,_),Baz(_,"X")) => println("found")
case _ => println("arrgh!")
}
f(Foo(Bar(1,"c"),Baz(new java.util.Date, "X"))) //--> found
f(Foo(Bar(1,"c"),Baz(new java.util.Date, "Y"))) //--> arrgh!
You can just specify the type in the matched pattern:
case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)
val someVal = WideLoad(...)
someVal match {
case w: WideLoad => w.d ! SomeMessage(...)
}
You can create a new case class which is a summary of your larger case class
case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)
case class WideLoadSummary(d: ActorRef, e: Date)
And then pattern match as normal.
val someVal = WideLoadSummary(wideload.d, wideload.e)
someVal match {
case WideLoadSummary(d, _) => d ! SomeMessage(...)
}