Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using new with Scala final case class

In Chapter 22 of "Programming in Scala" book, the :: class (cons) is defined as

final case class ::[T](hd: T, tl: List[T]) extends List[T] {
  //...
}

The :: method in class List is defined as follows:

def ::[U >: T](x: U): List[U] = new scala.::(x, this)

Why is the new required to create an instance of the finalcaseclass ::? Is this purely for disambiguation?

like image 616
Surjit Sen Avatar asked Dec 15 '11 02:12

Surjit Sen


People also ask

What is a final case class Scala?

If the class in Scala is final then it cannot inherit to derived class. Inheritance restriction will be added by final keyword. Here if class Shapes are final then its all members also final and cannot used in derived class.

Why does creating an instance of case class not require a new keyword?

Defining a case class Notice how the keyword new was not used to instantiate the Book case class. This is because case classes have an apply method by default which takes care of object construction. When you create a case class with parameters, the parameters are public val s. You can't reassign message1.

Can Scala case class have methods?

case classes automatically have equality and nice toString methods based on the constructor arguments. case classes can have methods just like normal classes.

Can you extend a case class in Scala?

Case classes can't be extended via subclassing. Or rather, the sub-class of a case class cannot be a case class itself. Final prevents extending a case class with anything ( i.e. case and not case classes), in addition to the built-in restriction to extend case classes with case classes.


2 Answers

With case classes you automatically get a companion object whose apply method calls the constructor, in the same way as you can do this with an ordinary class:

class Foo(val value: Int) 
object Foo { def apply(value: Int) = new Foo(value) }

val x = new Foo(42)  //
val y = Foo(42)      // both work the same

You can instantiate case classes with new if you want to. It might in theory be slightly faster because it doesn't have to go via the companion object's apply method, but I tried a quick benchmark and saw absolutely no difference in performance, so I guess it's optimised by the compiler, or just an immeasurably small difference compared to the actual construction.

So I don't think the new in the example you give has any significance and could just as well have been left out.

like image 53
Luigi Plinge Avatar answered Sep 28 '22 16:09

Luigi Plinge


You are correct; the new is not mandatory. They could have defined the instance method List#:: like this just as well:

def ::[U >: T](x: U): List[U] = scala.::(x, this)

(Note that we have:

type :: = collection.immutable.::
val  :: = collection.immutable.::

defined in the scala package object; the first is why your new scala.::(x, this) works, and the second is why my scala.::(x, this) works.)

The form the library uses calls the constructor directly, as yours does. The alternative calls the apply method of the synthetic companion object generated for the :: case class, which simply calls the constructor anyway. Perhaps calling the constructor was deemed clearer, or more efficient? (Efficiency gains should be close to nothing, though, since if the compiler doesn't inline the call to apply, the JVM will.) I suppose the most compact form:

def ::[U >: T](x: U) = ::(x, this)

could be mistaken for some wacky (i.e., impossible) sort of recursive invocation, and at any rate blurs the distinction between the class called :: and the List method called ::, which Prof. Odersky takes pains to keep separate in order to maximize reader comprehension.

Hope this helps.

like image 20
Harrison Avatar answered Sep 28 '22 15:09

Harrison