Overriding unapply method - scala

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

Related

How define the result type of this method?

How do I define the method return type in the following case:
working code
def deleteInstance(model: String, uid: Long) = model match {
case "menu" => Model.all(classOf[Menu]).filter("uid", uid).get().delete()
case "articles" => Model.all(classOf[Articles]).filter("uid", uid).get().delete()
case "news" => Model.all(classOf[News]).filter("uid", uid).get().delete()
case "image" =>Model.all(classOf[Image]).filter("uid", uid).get().delete()
case "files" =>Model.all(classOf[Files]).filter("uid", uid).get().delete()
case _ => false
}
non-working code:
class ModelManager{
def getModel(model: String) = {
model match{
case "menu" => classOf[Menu]
case "articles" => classOf[Articles]
case _ => false
}
def deleteInstance(model:String, uid: Long) = {
Model.all(getModel(model)).filter("uid", uid).get().delete()
}
}
}
Error raised is:
recursive method getModel needs result
type
It looks like you need an Option:
class ModelManager{
def getModel(model: String) = model match {
case "menu" => Some(classOf[Menu])
case "articles" => Some(classOf[Articles])
case _ => None
}
def deleteInstance(model:String, uid: Long) =
getModel(model) map { m =>
Model.all(m).filter("uid", uid).get().delete()
} getOrElse false
}
You can think of an Option as a container that can hold at most one element. The Option that holds an element x is Some(x). The empty Option is None. Option has several useful methods, including the map and getOrElse methods used above.
The map method applies a function to each element of the "container". Of course, if the container is None, it does nothing (except perhaps to change the static type of the Option). In your case (assuming delete returns a Boolean), the map method will change the Option[Class] into an Option[Boolean].
The getOrElse method returns the element of the option, if there is one, and otherwise returns a default value (false in this case).
Note that you can also simplify your implementation by using the condOpt method defined in PartialFunction:
class ModelManager{
def getModel(model: String) = condOpt(model) {
case "menu" => classOf[Menu]
case "articles" => classOf[Articles]
}
def deleteInstance(model:String, uid: Long) =
getModel(model) map { m =>
Model.all(m).filter("uid", uid).get().delete()
} getOrElse false
}
It looks like getModel will return a Class sometimes, a Boolean others. In Scala, this would typically be modeled using the Either class:
def getModel(model: String) = {
model match{
case "menu" => Left(classOf[Menu])
case "articles" => Left(classOf[Articles])
case _ => Right(false)
}
Left and Right represent the two possible choices of an Either. Callers of this method will need to inspect the return value (probably by using pattern matching as well) to decide if the method returned a Class or Boolean.
It seems you didn't close with parens in the right place. Did you mean this?
class ModelManager{
def getModel(model: String) = {
model match{
// snip
}
} // end method here
def deleteInstance(model:String, uid: Long) = {
Model.all(getModel(model)).filter("uid", uid).get().delete()
}
}
It does not look like you're trying to define a recursive method... Then you're likely to have other issues to resolve as you need a method that returns Class[_] not a combination of Boolean and Class[_] (which would be Any). So may be this would work better?
def getModel(model: String): Class[_] = {
model match{
case "menu" => classOf[Menu]
case "articles" => classOf[Articles]
} // end method here

Accessing static members of case classes

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.

Can I use a class variable in a Scala match statement?

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)

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(...)
}

Applying overloaded, typed methods on a collection

I'm quite new to Scala and struggling with the following:
I have database objects (type of BaseDoc) and value objects (type of BaseVO). Now there are multiple convert methods (all called 'convert') that take an instance of an object and convert it to the other type accordingly - like this:
def convert(doc: ClickDoc): ClickVO = ...
def convert(doc: PointDoc): PointVO = ...
def convert(doc: WindowDoc): WindowVO = ...
Now I sometimes need to convert a list of objects. How would I do this - I tried:
def convert[D <: BaseDoc, V <: BaseVO](docs: List[D]):List[V] = docs match {
case List() => List()
case xs => xs.map(doc => convert(doc))
}
Which results in 'overloaded method value convert with alternatives ...'. I tried to add manifest information to it, but couldn't make it work.
I couldn't even create one method for each because it'd say that they have the same parameter type after type erasure (List).
Ideas welcome!
Don't think you need the generic parameters on the method. List is already covariant in Scala. You also don't need the pattern match - map will transform an empty list to itself. How about something like this:
def convert(docs: List[BaseDoc]):List[BaseVO] = docs map {
case doc: ClickDoc => convert(doc)
case doc: PointDoc => convert(doc)
case doc: WindowDoc => convert(doc)
}
The other option is to move the convert methods to the various BaseDoc subclasses and allow it to be called polymorphically.
You need to do type-casting:
//case class MyBaseDoc(x: Int, y: Int)
//case class BaseVO(x: Int, y: Int)
//case class ClickDoc(x: Int, y: Int) extends MyBaseDoc(x, y)
//case class ClickVO(var x: Int, var y: Int) extends BaseVO(x, y)
def convert(doc: ClickDoc): ClickVO = doc match {
case null => null
case _ =>
val result = new ClickVO
result.x = doc.x
result.y = doc.y
result
}
def convert(docs: List[MyBaseDoc]):List[BaseVO] = docs match {
case List() => List()
case xs => xs.map(doc => convert(doc.asInstanceOf[ClickDoc]))
}