If I have case class defined as below
case class Calculator(brand: String, model: String)
How does it's companion object's unapply method would look like? What type of arguments would it take?
I am not able to emulate this by defining a class and then it's companion object by myself.
class abc (age:Int, name:String) {
}
object abc {
def apply(age:Int, name:String) = new abc(age, name)
def unapply(obj:abc) = Some("test")
}
abc(1, "aaaa")
res6: abc = abc#269f4bad
res6 match {
| case abc(1, "aaaa") => println("found")
| }
error: too many patterns for object abc offering String: expected 1, found 2
case abc(1, "aaaa") => println("found")
^
<console>:14: error: type mismatch;
found : Int(1)
required: String
case abc(1, "aaaa") => println("found")
You'll need to make age and name members of your class so that they are accessible after construction (can be done by making them vals), and then use them in unapply:
class abc (val age:Int, val name:String)
object abc {
def apply(age:Int, name:String) = new abc(age, name)
def unapply(candidate: abc) = Some((candidate.age, candidate.name))
}
Which would match correctly:
scala> new abc(2, "bbbb") match {
| case abc(1, "aaaa") => println("found 1")
| case abc(2, "bbbb") => println("found 2")
| case _ => println("not found")
| }
found 2
Tzach beat me to it..
Your error message btw. is because of the mismatch between the Option your unapply returns (which contains a single string) and the match in your case statement (with wants to match against a pair).
You could include a null check just to make sure (the compiler does, for case class companion objects):
class Abc(val age: Int, val name: String)
object Abc {
def unapply(obj: Abc): Option[(Int, String)] =
if (obj == null)
None
else
Some((obj.age, obj.name))
}
Shameless self-promotion: If you're interested in more details of pattern matching, you might find my little presentation "Everything you always wanted to know about pattern matching" useful.
If you want to create a custom class which has a unapply method and want to use the Extractor Pattern with it, the following rules should apply:
The return type of an unapply should be chosen as follows:
If it is just a test, return a Boolean. For instance case even()
If it returns a single sub-value of type T, return an Option[T]
If you want to return several sub-values T1,...,Tn, group them in an optional
tuple Option[(T1,...,Tn)].
Generally, this means that for you example all that needs to be done with the unapply method is the third option, which returns a tuple of values. Following that, unlike the case class which automatically creates immutable fields for you, you'll need to add the val annotation to you class declaration.
class abc (val age: Int, val name: String)
And:
def unapply(obj: abc): Option[(Int, String)] = Some((obj.age, obj.name))
Related
Consider the code block.
class Evaluation {
def evaluate= {
println("Charlie...")
}
}
case class Dept(name:String) extends Evaluation
def depEval(name:String,f:(String) => Evaluation) ={
println(name)
f(name).evaluate
}
depEval("abc", Dept)
Why does Dept can be passed as a Funtion1 type? Is it that, Scala does not checks for the type before resolving the arguments.
Consider other snippet
def matchingCase(f: String => Evaluation)= {
println(f.toString())
f match {
case Dept => println("function type matched")
}
}
matchingCase((x: String)=> Dept(x))
Whereas in the above code scala gives a match error, as expected.
The companion object of a case class extends FunctionX[T1, T2, <CaseClass>] so that you can use it to construct instances of the case class.
So, for example, if you have a case class
case class Foo(i: Int, s: String)
the compiler will generate a companion object
object Foo extends (Int, String) ⇒ Foo with Product2[Int, String] {
def apply(i: Int, s: String) = new Foo(i, s)
// and some other stuff such as equals, hashCode, copy
}
This allows you to construct an instance of Foo like this:
Foo(42, "Hello")
instead of
new Foo(42, "Hello")
So, to conclude: the reason, why you can pass the Dept companion object as a function is because it is a function.
Dept is not a "case class type", it's the companion object for the case class. It extends Function1 (and has .apply method, that's part of the interface), so you can use it wherever a function is expected.
The second snipped fails because { x => Dept(x) } is it's own anonymous function, not Dept object.
It has a lot of sense, because if you just give as paramenter Dept it will behave ass a function that takes a String as parameter, and return an instance of the class Dept, meaning a Dept object (It is like give the new or apply function as parameter), look that the following code is valid also:
val c: Dept.type = Dept
val dep = c("Name1")
dep
res1: Dept = Dept(Name1)
dep.evaluate
Charlie...
res2: Unit = ()
i have following class hierarchy.
trait Item {val id: String}
case class MItem(override val id: String, val name: String) extends Item
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name)
val d = new DItem("1", "one", "another one")
println(d)
Expected Output
DItem(1, one, another one)
Actual Output
Mitem(1,one)
Why is this happening. What is recommended so that i get the real type of my object and the not type of super class.
This is not a type erasure. You are getting this result because DItem gets toString() implementation inherited from Mitem. You have to override it to get what you want
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
So here is a result:
scala> val d = new DItem("1", "one", "another one")
d: DItem = DItem(1, one, another one)
scala> println(d)
DItem(1, one, another one)
It is almost always a bad idea to inherit from case classes because besides toString successor class will also inherit equals and hashCode.
Another drawback is limited pattern-matching for such successor classes i.e it is impossible to use such classes in case branches and may lead to confusing errors.
Example
case class A(id: String)
class B(id: String, name: String) extends A(id)
new B("foo", "bar") match {
case A(id) => println(id)
case other => println(other)
}
You may expect that there is no error in this code, but you'll get
<console>:17: error: constructor cannot be instantiated to expected type;
found : A
required: B
case A(id) => println(id)
^
However if you'll infer a type for B instance explicitly it will work
scala> new B("foo", "bar").asInstanceOf[A] match {
| case A(id) => println(id)
| case other => println(other)
| }
foo
So... Inheriting from case classes is very error-prone and confusing and should be avoided unless you know what are you doing.
Inheriting from case classes is deprecated as far as I know. So case classes can (should) only inherit from regular classes.
doing println usually invoke toString on the object pass on it.
so what happen on your code is, it will invoke the toString implementation of the object, it happens to be MItem that has this implementation.
so you need to override the toString on DItem like this:
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
if you want to just get the type of the object you can use getClass.
println(d.getClass)
I want to use a Scala extractor to match my custom type with a String (in that specific order, not String to Scala). Here is my code:
class Scala
object Scala {
def apply(s: String) = new Scala
def unapply(sc: Scala) = Some("Scala")
}
class ExtendedString(s: String) {
def unapply(sc: Scala): Option[String] = {
Some(s)
}
}
implicit def str2Scala(s: String): ExtendedString = {
new ExtendedString(s)
}
Scala match {
case "abc" => println("Aha!")
case "def" => println("Yeah!")
case "scala" => println("Scala!")
}
But it does not work. I get an error:
Error:(20, 9) type mismatch;
found : String("abc")
required: A$A109.this.Scala.type
case "abc" => println("Aha!")
^
How can I fix it to make extractor work?
PS
My original idea was to provide an implicit converter to extend String class with ExtendedString class and then to implement unapply method there to make extraction possible.
There should be no need whatsoever for implicits if all you want is an extractor.
To define an extractor, you make an object with an unapply method. E.g.
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[ExtractedValue] = { ... }
}
Then you match a value using the extractor
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(extractedValue) => println(s"I got $extractedValue!")
}
If you want your extractor to come back with multiple values, the unapply should return an option of tuple:
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[(ResultType1, ResultType2, ...)] = { ... }
}
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(result1, result2, ...) => ...
}
I'm not clear on what you are trying to accomplish from your example code, so I'll make an example that maybe is relevant for you.
Let's say you make a custom Point class:
case class Point(x: Int, y: Int)
And you want to be able to extract points from a String. In this case, the ValueBeingMatched is a String, and the ExtractedValue is a Point. I'll also define the extractor in the Point object. As for functionality, let's assume that a string like "12,8" corresponds to a Point(12, 8).
object Point {
def unapply(s: String): Option[Point] = {
val parts = s.split(",")
// this is just example code, so I won't handle failures,
// but if it did fail, you'd return a None instead of Some
Some(Point(parts(0).toInt, parts(1).toInt))
}
}
Now that it's defined, you can match strings using the Point extractor:
val s = "3,4"
s match {
case Point(p) => // p is actually an instance of Point
}
edit to respond to feedback:
In order to match directly to a string, the value being matched must already be a String. So one way would be to add a converter method e.g.
instanceOfMyType.convertToString match {
case "abc" => println("Aha!")
}
Or you would have to write an extractor to allow
instanceOfMyType match {
case Extracted("abc") => println("Aha!")
}
I want to write a generic function functionChooser which will choose which function to use from a few options, based on a String argument.
This works:
def a (arg: String) = arg + " with a"
def b (arg: String) = arg + " with b"
def c (arg: String) = arg + " with c"
def functionChooser(func: String, additionalArg: String) = {
val f = func match {
case "a" => a _
case "b" => b _
case _ => c _
}
f(additionalArg)
}
scala> functionChooser("a", "foo")
res18: String = foo with a
I'm having trouble in making functionChooser generic, e.g. when functions a, b, and c return different case classes:
case class A(s: String)
case class B(s: String)
case class C(s: String)
def a (arg: String) = A(arg)
def b (arg: String) = B(arg)
def c (arg: String) = C(arg)
//functionChooser def as before
scala> functionChooser("a", "foo")
res19: Product with Serializable = A(foo)
I don't quite understand what I got there, I know I get an error when calling functionChooser("a", "foo").s ("error: value s is not a member of Product with Serializable").
Lastly, what I really want is that the functions would return Lists of these case classes, e.g.:
def a (arg: String) = List(A(arg))
def b (arg: String) = List(B(arg))
def c (arg: String) = List(C(arg))
So functionChooser should be generic to List[T] where T is some class.
The function functionChooser will return the most specific common super type of the case classes A, B and C. Since case classes inherit from Product and Serializable, the common super type is Product with Serializable.
If you want to access the case class field s you either have to cast the result, via pattern matching, or you provide a common base class of all your classes A, B and C which allows you to access the field.
trait Base {
def s: String
}
case class A(s: String) extends Base
case class B(s: String) extends Base
case class C(s: String) extends Base
With this type definition the return type of functionChooser would be Product with Serializable with Base and, thus, the result would allow you to access s.
If your function a, b and c return a List of the respective case class, then the return type of functionChooser would be List[Product with Serializable with Base].
Update
If you cannot change the class hierarchy, then you either have to cast the result or you could extract the necessary information in the functionChooser and wrap it in another type which contains the super set of all data you need. E.g.
def functionChooser(func: String, additionalArg: String): String = {
val f = func match {
case "a" => (a _).s
case "b" => (b _).s
case _ => (c _).s
}
f(additionalArg)
}
Note: Here I only extract the field s which is the super set of all required information.
You should return the upper common type for all three functions. Object (AnyRef) always fit.
def functionChooser(func: String, additionalArg: String) : AnyRef = {
In your case, where all possible returning values are Lists you may use more specific type:
def functionChooser(func: String, additionalArg: String) : List[_] = {
Of course that will eliminate type information. Any method should return the same type, it could not be polymorphic on it. So, you need to use .asInstanceOf[T] case further, to get this information back.
That make sense, because the actual type is unknown during runtime. e.g. the dispatcher string may be entered by user. If it would be known during compile time, then you could just use appropriate method without referring to descriptive string.
If you would like to get some common behaviour for all possible return types, than you should define a common trait for them and place common methods to it.
I want to be able to refer to a list that contains subtypes and pull elements from that list and have them implicitly casted. Example follows:
scala> sealed trait Person { def id: String }
defined trait Person
scala> case class Employee(id: String, name: String) extends Person
defined class Employee
scala> case class Student(id: String, name: String, age: Int) extends Person
defined class Student
scala> val x: List[Person] = List(Employee("1", "Jon"), Student("2", "Jack", 23))
x: List[Person] = List(Employee(1,Jon), Student(2,Jack,23))
scala> x(0).name
<console>:14: error: value name is not a member of Person
x(0).name
^
I know that x(0).asInstanceOf[Employee].name but I was hoping there was a more elegant way with types. Thanks in advance.
The best way is to use pattern matching. Because you are using a sealed trait the match will be exhaustive which is nice.
x(0) match {
case Employee(id, name) => ...
case Student(id, name, age) => ...
}
Well, if you want the employees, you could always use a collect:
val employees = x collect { case employee: Employee => employee }