How does case class in scala works internally - scala

I have 1 question regarding case class of scala
scala> case class Person(name:String, age:Int)
scala> val person1 = Person("Posa",30)
scala> val person2 = new Person("Posa",30)
In what perspective both the objects (person1 and person2) differs ?
What difference does new keyword plays here?

When you create a case class
case class Person(name: String, age: Int)
compiler automatically creates a companion object which has a factory apply method
object Person {
def apply(name: String, age: Int): Person = new Person(name, age)
}
which is invoked when new is not used, so
val person1 = Person("Posa", 30) // function call creation syntax via companion's apply method
is equivalent to
val person1 = Person.apply("Posa", 30)
On the other hand instance creation expression is a constructor invocation on Person class (not its companion object)
val person2 = new Person("Posa", 30) // invokes constructor
Note starting Scala 3 even regular classes can be instantiated using function call creation syntax
Scala case classes generate apply methods, so that values of case
classes can be created using simple function application, without
needing to write new. Scala 3 generalizes this scheme to all concrete
classes.

Part of what the Scala compiler does (except in the case of an abstract case class) with a case class is that it generates a companion object for the class with an apply method with the same arguments as the case class's constructor and the case class as a result type.
When you write val person1 = Person("Posa", 30), that is exactly the same as writing
val person1 = Person.apply("Posa", 30)
The automatically generated apply method is effectively:
object Person {
def apply(name: String, age: Int): Person =
new Person(name, age)
}
Thus Person("Posa", 30) and new Person("Posa", 30) are the same thing.
For an abstract case class, the compiler will not generate an apply method in the companion object and will not generate a copy method in the case class, thus requiring an explicit new to construct an instance of a case class. This is not uncommonly used in an implementation pattern to ensure that:
only instances of a case class which satisfy domain constraints can be constructed
attempts to construct using apply will not throw an exception
For example, if we wanted to enforce that Persons must have a non-empty name and a non-negative age, we could
sealed abstract case class Person private[Person](name: String, age: Int) {
def copy(name: String = name, age: Int = age): Option[Person] =
Person(name, age)
}
object Person {
def apply(name: String, age: Int): Option[Person] =
if (name.nonEmpty && age >= 0) Some(unsafeApply(name, age))
else None
private[Person] def unsafeApply(name: String, age: Int) =
new Person(name, age) {}
}
In this, we've effectively renamed the compiler-generated apply to unsafeApply and made it private to the Person class and companion object (where we presumably can know that we're not violating a domain constraint, e.g. we could have a method in Person
def afterNextBirthday: Person = Person.unsafeApply(name, age + 1)
(since we're not changing the already validated name, and if we had a valid age (ignoring overflow...), adding 1 will not invalidate the age)). Our apply method now expresses that not all combinations of name and age are valid.

Related

Are constructor parameters treated differently in classes vs. case classes?

In the book, "Programming in Scala 5th Edition", it is mentioned in the fourth chapter that we can create class' objects using the following way if a class is a case class:
scala> case class Person(name: String, age: Int)
// defined case class Person
scala> val p = Person("Sally", 39)
val p: Person = Person(Sally,39)
I do not find it different from the following normal way:
scala> class Person(name: String, age: Int)
// defined class Person
scala> val p = Person("Aviral", 24)
val p: Person = Person#7f5fcfe9
I tried accessing the objects in both the cases and there was a difference. When I declare the same class as a case class, I can access its members: p.name, p.age whereas if I try to do the same with a normally declared class, I get the following error:
1 |p.name
|^^^^^^
|value name cannot be accessed as a member of (p : Person) from module class rs$line$3$.
How are the two cases different as far as constructing an object is considered?
As the Tour Of Scala or the Scala 3 book says
When you create a case class with parameters, the parameters are public vals.
Therefore, in
case class Person(name: String, age: Int)
both name and age will be public, unlike in an ordinary class.

Extend case class from another case class

I have two case class Person and Employee
case class Person(identifier: String) {}
case class Employee (salary: Long) extends Person {}
I am getting following error:
Unspecified value parameters: identifier: String
Error: case class Employee has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes
I am new to Scala and not able to understand what I have to do.
Version:
Scala : 2.11
Unfortunately, I'm afraid it is not possible for case class to extend another case class.
The inheritance in "plain" classes would look like:
class Person(val identifier: String) {}
class Employee(override val identifier: String, salary: Long)
extends Person(identifier) {}
val el = new Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
If you would like to achieve a similar effect with case classes, you would need to reach out to traits:
trait Identifiable {
def identifier: String
}
case class Person(identifier: String) extends Identifiable {}
case class Employee(identifier: String, salary: Long)
extends Identifiable {}
val el = Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
Defining extractors
Extractor provides a way for defining a matching statement used in pattern matching. It is defined in an object in unaply method.
Let's consider the first example again adding support for extractors:
class Person(val identifier: String)
class Employee(override val identifier: String, val salary: Long)
extends Person(identifier)
object Person {
def unapply(identifier: String): Option[Person] = {
if (identifier.startsWith("PER-")) {
Some(new Person(identifier))
}
else {
None
}
}
}
object Employee {
def unapply(identifier: String): Option[Employee] = {
if (identifier.startsWith("EMP-")) {
Some(new Employee(identifier, 999))
}
else {
None
}
}
}
Now, let's define a method that will define pattern matching using those extractors:
def process(anInput: String): Unit = {
anInput match {
case Employee(anEmployee) => println(s"Employee identified ${anEmployee.identifier}, $$${anEmployee.salary}")
case Person(aPerson) => println(s"Person identified ${aPerson.identifier}")
case _ => println("Was unable to identify anyone...")
}
}
process("PER-123-test") // => Person identified PER-123-test
process("EMP-321-test") // => Employee identified EMP-321-test, $999
process("Foo-Bar-Test") // => Was unable to identify anyone...
Case classes in Scala add several different features but often you really use only some of them. So the main question you need to answer is which features you really need. Here is a list based on the spec:
remove the need to type val before field names/constructor params
remove the need for new by adding apply method to the companion object
support for pattern matching by adding unapply method to the companion object. (One of nice things of Scala is that pattern-matching is done in a non-magical way, you can implement it for any data type without requiring it to be a case class)
add equals and hashCode implementations based on all the fields
add toString implementations
add copy method (useful because case classes are immutable by default)
implement Product trait
A reasonable guess of the equivalent for case class Person(identifier: String) is
class Person(val identifier: String) extends Product {
def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
override def equals(other: Any): Boolean = other match {
case that: Person => (that canEqual this) && identifier == that.identifier
case _ => false
}
override def hashCode(): Int = identifier.hashCode
override def toString = s"Person($identifier)"
def copy(newIdentifier: String): Person = new Person(newIdentifier)
override def productElement(n: Int): Any = n match {
case 0 => identifier
case _ => throw new IndexOutOfBoundsException(s"Index $n is out of range")
}
override def productArity: Int = 1
}
object Person {
def apply(identifier: String): Person = new Person(identifier)
def unapply(person: Person): Option[String] = if (person eq null) None else Some(person.identifier)
}
case class Employee(override val identifier: String, salary: Long) extends Person(identifier) {}
Actually the main objections to inheriting from a case class and especially making a case class inheriting another one are the Product trait, copy and equals/hashCode because they introduce ambiguity. Adding canEqual partially mitigates the last problem but not the first ones. On the other hand in a hierarchy like yours, you probably don't need the copy method or Product implementation at all. If you don't use Person in pattern matching, you don't need unapply as well. Most probably all you really need case for is apply, toString and hashCode/equals/canEqual.
Inheriting from case classes (even with regular non-case classes, which is not prohibited) is a bad idea. Check this answer out to get an idea why.
You Person does not need to be a case class. It actually does not need to be a class at all:
trait Person {
def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person

why does Scala function accepts case class type for a function argument?

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 = ()

How to use macro annotation on the companion object of case class to generate val for each accessor based on their type

I am trying to generate Lens for all accessors of a case class. Ideally, I would like to obtain something like:
case class Person(_name: String, _age: Int)
#Lenses
object Person
This would generate the following companion object:
object Person {
val name: Lens[Person, String] = // some implementation
val age: Lens[Person, Int] = // some implementation
}
I found this project that uses macro annotation on companion object to generate a method for each accessor. However, I dont know how to get the type of each accessor and the type of the case class.

Why are constructor parameters made into members for case classes?

{
class MyClass(name: String) {}
val x = new MyClass("x")
println(x.name) // Error name is not a member of MyClass
}
but
{
abstract class Base
case class MyClass(name: String) extends Base {}
var x = new MyClass("x")
println(x.name) // name is a member of MyClass
}
So, what's the deal with case classes? Why are all of the constructor parameters turned into variables.
name is member in both examples, but private in your first example while public in your second. Case classes make their constructor parameters public val by default.
Pattern matching is the most important but not the only application for case classes. Another important point is that they implement the equals and hashCode methods in terms of the constructor arguments (aka product elements). Therefore, case classes are very useful for defining data structures that serve as elements in sets or keys in maps. That in turn only makes sense if these elements are visible.
Compare:
class Foo(val i: Int)
val set1 = Set(new Foo(33))
set1.contains(new Foo(33)) // false!!
And:
case class Bar(val i: Int)
val set2 = Set(Bar(33)
set2.contains(Bar(33)) // true!
Two case class instances with equal parameters are equal themselves. You can imagine them representing some "constants". This implies that you should not have mutable state in them.
You can, however, use a second parameter list to exclude arguments from the equality:
case class Baz(i: Int)(val n: Long)
Baz(33)(5L) == Baz(33)(6L) // true!
Another useful feature which implies that the constructor arguments become values, is making copies. This is the way immutable data is changes—you create a new instance with a particular value changed, leaving the original value in place.
case class Person(name: String, age: Int)
val p1 = Person("Fuzzi", 33)
val p2 = p1.copy(age = 34)
The copy method uses default values for all unspecified argument, taking those values from the constructor args.
Just to be clear, the constructor arguments aren't used to create variables, they're used to create values.
If you specify val in your first example, the non-case class:
class MyClass(val name: String) {}
then you also get the argument translated into a public value, the same as is done for the case class.
In the example on the Scala-Lang site it says:
It makes only sense to define case classes if pattern matching is used
to decompose data structures. The following object defines a pretty
printer function for our lambda calculus representation:
followed by the example code:
object TermTest extends Application { def printTerm(term: Term) {
term match {
case Var(n) =>
print(n)
case Fun(x, b) =>
print("^" + x + ".")
printTerm(b)
case App(f, v) =>
Console.print("(")
printTerm(f)
print(" ")
printTerm(v)
print(")")
} } def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false } val id = Fun("x", Var("x")) val t = Fun("x", Fun("y", App(Var("x"), Var("y")))) printTerm(t) println println(isIdentityFun(id)) println(isIdentityFun(t)) }
To add something to my comment due to lack of available space: consider the following example case class:
case class My(x: Int)
If you save it to file and pass it to scalac -print, you get following expanded code (I removed unimportant stuff):
case class My extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def x(): Int = My.this.x;
Notice <caseaccessor>s here.
And then companion object:
<synthetic> object My extends runtime.AbstractFunction1 with Serializable {
case <synthetic> def apply(x: Int): My = new My(x);
case <synthetic> def unapply(x$0: My): Option = if (x$0.==(null))
scala.this.None
else
new Some(scala.Int.box(x$0.x()));
case <synthetic> <bridge> def apply(v1: Object): Object = My.this.apply(scala.Int.unbox(v1));
//...
Notice apply and unapply here. If you look at complete output yourself, you'll learn more about how scala generates your code.