Accessing static members of case classes - scala

I have some code
case class A(s:String) {val x = "hello"}
Why can't I access the static variable x without instantiating the class A? If I type
A.x
I get the following error:
error: value x is not a member of object A
Edit:
I missed out mentioning the remaining code. Here is the example that I would like to use:
abstract class A { val name:String }
case class B(i:Int) extends A { val name = "B" }
case class C(b:Boolean) extends A { val name = "C" }
def getType(s:String) = s match {
case B.name => println ("Object B")
case C.name => println ("Object C")
}
The error:
scala> def getType(s:String) = s match {
| case B.name => println ("Object B")
| case C.name => println ("Object C")
| }
<console>:11: error: value name is not a member of object B
case B.name => println ("Object B")
^
<console>:12: error: value name is not a member of object C
case C.name => println ("Object C")
^
As to why use case classes, the case classes are not defined for this purpose. Elsewhere I have some code like:
def func(a:A) = a match {
case b:B =>
case c:C =>
...
}

Well, you cannot call the "static" variable x, because in Scala there are no static variables. You are declaring x to be a regular member of class A, which you could access if you had an instance of class A.
What you try to do by calling A.x is accessing a value with the name "A". There happens to be such a value in scope - the compiler generated companion object for your case class A.
But this object A has no member "x", therefore the compiler rightly complains about it.
You can add the value x to the object A instead of the class/type A by doing the following:
case class A(s:String)
object A { val x = "hello" }

From the small amount you described of the problem, it sounds like case classes are just not for you.
Alternate patterns include...
Constants:
val Abs1 = "1" //note that it starts with an uppercase letter
val s: String = ...
s match {
case Abs1 => ...
case _ =>
}
Extractors:
object Positive {
def unapply(i: Int): Option[Int] = if(i >= 0) Some(i) else None
}
val i: Int = ...
i match {
case Positive(p) => ... //p will be bound to the matched positive number
case _ => ...
}
Case Classes (used properly):
case class MyType(s: String)
val x: MyType = ...
x match {
case MyType("a") => ...
case MyType("b") => ...
case MyType(matched) => ...
//matched will be bound to whatever string was used to construct the MyType instance
}
Case Objects:
abstract sealed trait Foo { def s: String }
case object Bar extends Foo { val s = "I'm a Bar" }
case object Baz extends Foo { val s = "I'm a Baz" }
val x: Foo = ...
x match {
case Bar => ...
case Baz => ...
//no other possibilities because Foo is sealed
}

Leaving aside issues of design for a moment.
If you need a workaround then you can bind an identifier to a matched case class or case object and use the bound identifier to access members of the matched entity. The bound identifier can be used in guard statements in the match or in the action provided for the match:
case class Bob(x: String, y: String) {val z = "Bragging"}
val bob1 = Bob("Big", "Bad")
bob1 match {
case b # Bob(x, y) if b.z == "Bragging" => println("Got "+x+", "+y+", "+b.z+" Bob!")
case _ =>
}
case object Bob {val z = "Balding"}
val bob2 = Bob
bob2 match {
case b # Bob if b.z == "Balding" => println("Got "+b.z+" Bob!")
case _ =>
}
Returning to design, in your case class definition you declare 'name' in the constructor body of B but you would get more useability from having 'name' as a parameter:
case class B(i: Int, name: String) extends A
Which could then match like this:
def getType(s:String) = s match {
case B(_, name) => println ("Object B("+name+")")
...
Finally it's hard to say without further detail but I suspect that mapping case classes to a large set of similar entities on a one to one basis is perhaps not the best choice, better to use case objects, or instances of a limited number of case classes or even tuples.

Related

case insensitive pattern match in Scala

I want to do pattern matching in Scala but it should be case insensitive. Is there a way I can write the code without using separate 'case' clauses for lower and upper cases
//person class with first name and last name
case class Person (var fn: String, val ln: String) {
val name = fn
val lastName = ln
}
//two instances. Same last name but cases are different
val a2 = Person("Andy","Cale")
val a3 = Person("Andy","cale")
def isCale(person:Person) {
person match {
//I want that this case should be case insensitive
case Person(_,"Cale") => println("last-name Cale")
case _ => println("not Cale")
}
}
isCale(a2)
lastname Cale
//I want this to also match
isCale(a3)
not Cale
One alternative is to extract the last name and compare as follows but I am interested in finding if there is a way to do this in case itself.
def isCale(a2:A2) {
val s = a2.ln
s.toLowerCase match {
case "cale" => println("last-name Cale")
case _ => println("not Cale")
}
You can use a guard:
def main(args: Array[String]): Unit = {
case class Person(firstName: String, lastName: String)
val p = Person("Yuval", "Itzchakov")
p match {
case Person(_, lastName) if lastName.equalsIgnoreCase("itzchakov") =>
println(s"Last name is: $lastName")
case _ => println("Not itzchakov")
}
}
Side note - case class parameters will be attached as vals on the declared class, there's no need for the additional assignment and no need for the val/var definition on the constructor.
You can use an extractor:
scala> val r = "(?i:it.*ov)".r
r: scala.util.matching.Regex = (?i:it.*ov)
scala> case class Person(firstName: String, lastName: String)
defined class Person
scala> val ps = Person("Fred", "Itchikov") :: Person("Yuval", "Itzchakov") :: Nil
ps: List[Person] = List(Person(Fred,Itchikov), Person(Yuval,Itzchakov))
scala> ps collect { case Person(_, n # r()) => n }
res0: List[String] = List(Itchikov, Itzchakov)

Can a Scala case object be used in a match case

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

Overriding unapply method

I have a case from a library class and I want to override unapply method to reduce the number of parameters I need to pass to do pattern matching against it. I do this:
object ws1 {
// a library class
case class MyClass(a: Int, b: String, c: String, d: Double /* and many more ones*/)
// my object I created to override unapply of class MyClass
object MyClass {
def unapply(x: Int) = Some(x)
}
val a = new MyClass(1, "2", "3", 55.0 /* and many more ones*/)
a match {
case MyClass(x /*only the first one is vital*/) => x // java.io.Serializable = (1,2,3,55.0)
case _ => "no"
}
}
But I want it to return just 1. What's wrong with this?
case class MyClass(a: Int, b: String, c: String, d: Double /* and many more ones*/)
object MyClassA {
def unapply(x: MyClass) = Some(x.a)
}
val a = new MyClass(1, "2", "3", 55.0 /* and many more ones*/)
a match {
case MyClassA(2) => ??? // does not match
case MyClassA(1) => a // matches
case _ => ???
}
You cannot define your custom unapply method in the MyClass object, because it would have to take a MyClass parameter, and there's already one such method there – one generated automatically for the case class. Therefore you have to define it in a different object (MyClassA in this case).
Pattern matching in Scala takes your object and applies several unapply and unapplySeq methods to it until it gets Some with values that match the ones specified in the pattern.
MyClassA(1) matches a if MyClassA.unapply(a) == Some(1).
Note: if I wrote case m # MyClassA(1) =>, then the m variable would be of type MyClass.
Edit:
a match {
case MyClassA(x) => x // x is an Int, equal to a.a
case _ => ???
}
I would ditch the overloaded unapply and just use the following for the match:
a match {
case MyClass(x, _, _, _) => x // Result is: 1
case _ => "no"
}
EDIT :
If you really want to avoid the extra underscores, I think you'll need to look at something like:
a match {
case x:MyClass => x.a
case _ => "no"
}

Can I use abstract types in matching of case classes?

Or, in other words:
Can I verify with matching if elements in a tuple are of the same case class, despite having different values in theirs fields (arguments)?
Is there something equivalent to the case[T] below?
sealed abstract class RootClass
case class ChildClassX(valuex: Boolean) extends RootClass
case class ChildClassY(valuey: Boolean) extends RootClass
// and other case classes here...
object Foo {
def compare(a: RootClass, b: RootClass) = {
(a, b) match {
case[T] (T(a), T(b)) => a == b
case _ => throw Exception("a and b should be of same child classes.")
}
}
I hope I dont have to do:
object Foo {
def compare(a: RootClass, b: RootClass) = {
(a, b) match {
case (ChildClassX(a), ChildClassX(b)) | (ChildClassY(a), ChildClassY(b)) | (ChildClassZ(a), ChildClassZ(b)) | etc. => a == b
case _ => throw Exception("a and b should be of same child classes.")
}
}
Related:
matching
The most reasonable solution that I can think of is to simply compare the two items' classes.
(a, b) match {
case (x,y) if x.getClass == y.getClass => "matching classes"
case _ => "no match"
}
I am not aware of any construct that works the way you describe, like case[T].
This would be a solution, I guess - if it's really only about the classes:
object Foo {
def compare[A,B](a: A, b: B) =
if (a.getClass.getSuperclass != b.getClass.getSuperclass)
throw new MatchError("a and b should be of same child classes.")
else (a.getClass == b.getClass)
}
No matching involved... Maybe someone has a more elegant solution? But this is maybe the shortest...
Example test code:
object ObjCmp extends App {
case object X
val p: Product = ChildClassX(true)
println(Foo.compare(ChildClassX(true), ChildClassX(false)))
println(Foo.compare(ChildClassX(true), ChildClassY(false)))
println(Foo.compare(ChildClassX(true), p))
println(Foo.compare(ChildClassX(true), X))
}
prints:
true
false
true
Exception in thread "main" scala.MatchError: a and b should be of same child classes.

Match multiple cases classes in scala

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"
}