I am constructing a List in Scala as below:
val list1 = List(1,2,3) //This is otherwise: List.apply(1,2,3) ---> A
Now I have the below line.
list1(1) //which is otherwise list1.apply(1) ---> B
Above line returns 2 which is of type Int.
Line A and B are calls to apply method in the List class. Method overloading certainly can not be present in List class. Then based on what the compiler treats A and B differently ?
Can anyone please help me to understand this.
Thanks!
There are two different types at play here. In your second example, you are calling the apply instance method on your instance of the List class. In your first example, you aren't calling anything on the List class at all, you are calling the apply method on the List companion object.
in the line
val list1 = List(1,2,3)
You can tell that in this context you are calling a method on the companion object because you don't have an instance of List and are seemingly calling apply on the class itself.
There are two apply methods here: the List.apply and LinearSeqOptimized.apply. The main difference is, when you call List(1, 2, 3) you're using List companion object apply method and, when you call list1(1) you're using the apply's method inherited from LinearSeqOptimized trait (which List class inherits from).
Above answers explains the reasons well. Let me explain it in more details.
When you invoke val list1 = List(1,2,3), you are invoking a object List which is a companion object of class List and which in turns invoke .apply() method and return instance of class List.
override def apply[A](xs: A*): List[A] = xs.toList //return instance of class List.
Now, if you look into class List:
sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqOptimized[A, List[A]] {...}
You can see that it inherit trait LinearSeqOptimized[A, List[A]]. And if you look into this trait you can see an apply() method as
/** Selects an element by its index in the $coll.
* Note: the execution of `apply` may take time proportional to the index value.
* #throws IndexOutOfBoundsException if `idx` does not satisfy `0 <= idx < length`.
*/
def apply(n: Int): A = {
val rest = drop(n)
if (n < 0 || rest.isEmpty) throw new IndexOutOfBoundsException("" + n)
rest.head
}
That means, class List will also inherit this .apply method. Therefore, when you invoke list1(1), you are actually invoking this apply method, which return particular index value of the list.
To conclude, in first code, you are invoking .apply method of companion object List which create list and return instance of class List and in second case, you are invoking .apply method of class List which return particular value of that list.
Related
In Scala, Nil is actually an object which returns an empty List. When I type, Nil, it prints the following.
res24: scala.collection.immutable.Nil.type = List()
res24 is the variable whose type is scala.collection.immutable.Nil.type. I don't quite understand the last piece 'type'. Where this is located ?
Another question is: Nil returns an empty list. How can we write an object which returns something? I tried below; it does not return the integer value what I expected.
object Sample { 123 }
val x = Sample
x: Sample.type = Sample$#36fe7b03
Thanks in advance!
I don't quite understand the last piece 'type'. Where this is located
?
This is what Scala refers to as the "Singleton Type". Nil and Sample share the common property of being objects in Scala which entails they are classes with a single instance (given that it is a top level declaration, and not nested inside a class, inside a given package scope). It part of the type definition, but it isn't visible in the declaration site. For more on the uses of the singleton type see What is a Singleton Type exactly?
How can we write an object which returns something?
Writing an object that returns something is similar to asking "Can I create a class that returns a value when instantiated?". object are singletons and can have methods on them that returns values:
object Bar {
def x(): Int = 42
}
def main(args: Array[String]): Unit = print(Bar.x())
Which yields 42.
Edit
Still I am not clear how the singleton object Nil, returning an empty
list
If we can simplify and mimic the List[A] definition, we'll see that:
sealed trait List[+A]
case class ::(head: A, tail: List[A]) extends List[A]
case object Nil extends List[Nothing]
What we have here is an Algebraic Data Type (ADT), more specifically a Sum Type, which is a type that's composed of multiple possible values. Here, a List[A] can be either a ::, or Nil, and in our case it represents the empty list case.
In Scala, Nil is actually an object which returns an empty List.
No, Nil is an object that represents the empty list (by extending from List[Nothing]). See https://alvinalexander.com/scala/what-is-difference-between-nil-empty-list-in-scala/
I am trying to understand how a case class can be passed as an argument to a function which accepts functions as arguments. Below is an example:
Consider the below function
def !![B](h: Out[B] => A): In[B] = { ... }
If I understood correctly, this is a polymorphic method which has a type parameter B and accepts a function h as a parameter. Out and In are other two classes defined previously.
This function is then being used as shown below:
case class Q(p: boolean)(val cont: Out[R])
case class R(p: Int)
def g(c: Out[Q]) = {
val rin = c !! Q(true)_
...
}
I am aware that currying is being used to avoid writing the type annotation and instead just writing _. However, I cannot grasp why and how the case class Q is transformed to a function (h) of type Out[B] => A.
EDIT 1 Updated !! above and the In and Out definitions:
abstract class In[+A] {
def future: Future[A]
def receive(implicit d: Duration): A = {
Await.result[A](future, d)
}
def ?[B](f: A => B)(implicit d: Duration): B = {
f(receive)
}
}
abstract class Out[-A]{
def promise[B <: A]: Promise[B]
def send(msg: A): Unit = promise.success(msg)
def !(msg: A) = send(msg)
def create[B](): (In[B], Out[B])
}
These code samples are taken from the following paper: http://drops.dagstuhl.de/opus/volltexte/2016/6115/
TLDR;
Using a case class with multiple parameter lists and partially applying it will yield a partially applied apply call + eta expansion will transform the method into a function value:
val res: Out[Q] => Q = Q.apply(true) _
Longer explanation
To understand the way this works in Scala, we have to understand some fundamentals behind case classes and the difference between methods and functions.
Case classes in Scala are a compact way of representing data. When you define a case class, you get a bunch of convenience methods which are created for you by the compiler, such as hashCode and equals.
In addition, the compiler also generates a method called apply, which allows you to create a case class instance without using the new keyword:
case class X(a: Int)
val x = X(1)
The compiler will expand this call to
val x = X.apply(1)
The same thing will happen with your case class, only that your case class has multiple argument lists:
case class Q(p: boolean)(val cont: Out[R])
val q: Q = Q(true)(new Out[Int] { })
Will get translated to
val q: Q = Q.apply(true)(new Out[Int] { })
On top of that, Scala has a way to transform methods, which are a non value type, into a function type which has the type of FunctionX, X being the arity of the function. In order to transform a method into a function value, we use a trick called eta expansion where we call a method with an underscore.
def foo(i: Int): Int = i
val f: Int => Int = foo _
This will transform the method foo into a function value of type Function1[Int, Int].
Now that we posses this knowledge, let's go back to your example:
val rin = c !! Q(true) _
If we just isolate Q here, this call gets translated into:
val rin = Q.apply(true) _
Since the apply method is curried with multiple argument lists, we'll get back a function that given a Out[Q], will create a Q:
val rin: Out[R] => Q = Q.apply(true) _
I cannot grasp why and how the case class Q is transformed to a function (h) of type Out[B] => A.
It isn't. In fact, the case class Q has absolutely nothing to do with this! This is all about the object Q, which is the companion module to the case class Q.
Every case class has an automatically generated companion module, which contains (among others) an apply method whose signature matches the primary constructor of the companion class, and which constructs an instance of the companion class.
I.e. when you write
case class Foo(bar: Baz)(quux: Corge)
You not only get the automatically defined case class convenience methods such as accessors for all the elements, toString, hashCode, copy, and equals, but you also get an automatically defined companion module that serves both as an extractor for pattern matching and as a factory for object construction:
object Foo {
def apply(bar: Baz)(quux: Corge) = new Foo(bar)(quux)
def unapply(that: Foo): Option[Baz] = ???
}
In Scala, apply is a method that allows you to create "function-like" objects: if foo is an object (and not a method), then foo(bar, baz) is translated to foo.apply(bar, baz).
The last piece of the puzzle is η-expansion, which lifts a method (which is not an object) into a function (which is an object and can thus be passed as an argument, stored in a variable, etc.) There are two forms of η-expansion: explicit η-expansion using the _ operator:
val printFunction = println _
And implicit η-expansion: in cases where Scala knows 100% that you mean a function but you give it the name of a method, Scala will perform η-expansion for you:
Seq(1, 2, 3) foreach println
And you already know about currying.
So, if we put it all together:
Q(true)_
First, we know that Q here cannot possibly be the class Q. How do we know that? Because Q here is used as a value, but classes are types, and like most programming languages, Scala has a strict separation between types and values. Therefore, Q must be a value. In particular, since we know class Q is a case class, object Q is the companion module for class Q.
Secondly, we know that for a value Q
Q(true)
is syntactic sugar for
Q.apply(true)
Thirdly, we know that for case classes, the companion module has an automatically generated apply method that matches the primary constructor, so we know that Q.apply has two parameter lists.
So, lastly, we have
Q.apply(true) _
which passes the first argument list to Q.apply and then lifts Q.apply into a function which accepts the second argument list.
Note that case classes with multiple parameter lists are unusual, since only the parameters in the first parameter list are considered elements of the case class, and only elements benefit from the "case class magic", i.e. only elements get accessors implemented automatically, only elements are used in the signature of the copy method, only elements are used in the automatically generated equals, hashCode, and toString() methods, and so on.
What exactly happens when you evaluate the expression: Seq(1,2,3)?
I am new to Scala and I am now a bit confused about the various collection types. Seq is a trait, right? So when you call it like this: Seq(1,2,3), it must be some kind of a companion object? Or not? Is it some kind of a class that extends Seq? And most importantly, what is the type of the returned value? Is it Seq and if yes, why is it not explicitly the extension class instead?
Also in the REPL I see that the contents of the evaluated expression is actually a List(1,2,3), but the type is apparently Seq[Int]. Why is it not an IndexedSeq collection type, like Vector? What is the logic behind all that?
What exactly happens when you evaluate expression: Seq(1,2,3)?
In Scala, foo(bar) is syntactic sugar for foo.apply(bar), unless this also has a method named foo, in which case it is a method call on the implicit this receiver, i.e. just like Java, it is then equivalent to this.foo(bar).
Just like any other OO language, the receiver of a method call alone decides what to do with that call, so in this case, Seq decides what to do.
Seq is a trait, right?
There are two Seqs in the standard library:
The trait Seq, which is a type.
The object Seq, which is a value.
So when you call it like that Seq(1,2,3) it must be some kind of a companion object? Or not?
Yes, it must be an object, since you can only call methods on objects. You cannot call methods on types, therefore, when you see a method call, it must be an object. Always. So, in this case, Seq cannot possibly be the Seq trait, it must be the Seq object.
Note that "it must be some kind of a companion object" is not true. The only thing you can see from that piece of code is that Seq is an object. You cannot know from that piece of code whether it is a companion object. For that, you would have to look at the source code. In this particular case, it turns out that it is, in fact, a companion object, but you cannot conclude that from the code you showed.
Is it some kind of a class that extends Seq?
No. It cannot possibly be a class, since you can only call methods on objects, and classes are not objects in Scala. (This is not like Ruby or Smalltalk, where classes are also objects and instances of the Class class.) It must be an object.
And most importantly what is the type of the returned value?
The easiest way to find that out is to simply look at the documentation for Seq.apply:
def apply[A](elems: A*): Seq[A]
Creates a collection with the specified elements.
A: the type of the collection's elements
elems: the elements of the created collection
returns a new collection with elements elems
So, as you can see, the return type of Seq.apply is Seq, or more precisely, Seq[A], where A is a type variable denoting the type of the elements of the collection.
Is it Seq and if yes, why is not explicitly the extension class instead?
Because there is no extension class.
Also, the standard design pattern in Scala is that the apply method of a companion object returns an instance of the companion class or trait. It would be weird and surprising to break this convention.
Also in REPL I see that the contents of the evaluated expression is actually a List(1,2,3), but the type is apparently Seq[Int].
The static type is Seq[Int]. That is all you need to know. That is all you can know.
Now, Seq is a trait, and traits cannot be instantiated, so the runtime type will be some subclass of Seq. But! You cannot and must not care, what specific runtime type it is.
Why is not an Indexed collection type, like Vector? What is the logic behind all that?
How do you know it is not going to return a Vector the next time you call it? It wouldn't matter one bit, since the static type is Seq and thus you are only allowed to call Seq methods on it, and you are only allowed to rely on the contract of Seq, i.e. Seq's post-conditions, invariants, etc. anyway. Even if you knew it was a Vector that is returned, you wouldn't be able to do anything with this knowledge.
Thus, Seq.apply returns the simplest thing it can possibly return, and that is a List.
Seq is the val of:
package object scala {
...
val Seq = scala.collection.Seq
...
}
it points to object scala.collection.Seq:
/** $factoryInfo
* The current default implementation of a $Coll is a `List`.
* #define coll sequence
* #define Coll `Seq`
*/
object Seq extends SeqFactory[Seq] {
/** $genericCanBuildFromInfo */
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
def newBuilder[A]: Builder[A, Seq[A]] = immutable.Seq.newBuilder[A]
}
and when you do Seq(1,2,3) the apply() method is ivoked from scala.collection.generic.GenericCompanion abstract class:
/** A template class for companion objects of "regular" collection classes
* represent an unconstrained higher-kinded type. Typically
* such classes inherit from trait `GenericTraversableTemplate`.
* #tparam CC The type constructor representing the collection class.
* #see [[scala.collection.generic.GenericTraversableTemplate]]
* #author Martin Odersky
* #since 2.8
* #define coll collection
* #define Coll `CC`
*/
abstract class GenericCompanion[+CC[X] <: GenTraversable[X]] {
...
/** Creates a $coll with the specified elements.
* #tparam A the type of the ${coll}'s elements
* #param elems the elements of the created $coll
* #return a new $coll with elements `elems`
*/
def apply[A](elems: A*): CC[A] = {
if (elems.isEmpty) empty[A]
else {
val b = newBuilder[A]
b ++= elems
b.result()
}
}
}
and finally, this method builds an object of Seq type by code mentioned above
And most importantly what is the type of the returned value?
object MainClass {
def main(args: Array[String]): Unit = {
val isList = Seq(1,2,3).isInstanceOf[List[Int]]
println(isList)
}
}
prints:
true
So, the type is scala.collection.immutable.List
Also in REPL I see that the contents of the evaluated expression is actually a List(1,2,3), but the type is apparently Seq[Int].
The default implementation of Seq is List by the code mentioned above.
Why is not an Indexed collection type, like Vector? What is the logic behind all that?
Because of immutable design. The list is immutable and to make it immutable and have a constant prepend operation but O(n) append operation cost and O(n) cost of accessing n'th element. The Vector has a constant efficient implementation of access and add elements by id, prepend and append operations.
To have a better understanding of how the List is designed in Scala, see https://mauricio.github.io/2013/11/25/learning-scala-by-building-scala-lists.html
I know that when you type:
val list = List(2,3)
you are accessing the apply method of the List object which returns a List. What I can't understand is why is this possible when the List class is abstract and therefore cannot be directly instanciated(new List() won't compile)?
I'd also like to ask what is the difference between:
val arr = Array(4,5,6)
and
val arr = new Array(4, 5, 6)
The List class is sealed and abstract. It has two concreate implementations
Nil which represents an empty list
::[B] which represents a non empty list with head and tail. ::[B] in the documentation
When you call List.apply it will jump through some hoops and supply you with an instance of the ::[B] case class.
About array: new Array(4, 5, 6) will throw a compile error as the constructor of array is defined like this: new Array(_length: Int). The apply method of the Array companion object uses the arguments to create a new instance of an Array (with the help of ArrayBuilder).
I started writing that the easy way to determine this is to look at the sources for the methods you're calling, which are available from the ScalaDoc. However, the various levels of indirection that are gone through to actually build a list give lie to the term 'easy'! It's worth having a look through if you want, starting from the apply method in the List object which is defined as follows:
override def apply[A](xs: A*): List[A] = xs.toList
You may or may not know that a parameter of the form xs : A* is treated internally as a Seq, which means that we're calling the toList method on a Seq, which is defined in TraversableOnce. This then delegates to a generic to method, which looks for an implicit
CanBuildFrom which actually constructs the list. So what you're getting back is some implementation of List which is chosen by the CanBuildFrom. What you actually get is a scala.collection.immutable.$colon$colon, which implements a singly-linked list.
Luckily, the behaviour of Array.apply is a little easier to look up:
def apply[T: ClassTag](xs: T*): Array[T] = {
val array = new Array[T](xs.length)
var i = 0
for (x <- xs.iterator) { array(i) = x; i += 1 }
array
}
So, Array.apply just delegates to new Array and then sets elements appropriately.
I think I see the merit in defining auxiliary constructors in such a way that the primary constructor is the solitary point of entry to the class. But why can't I do something like this?
class Wibble(foo: Int, bar: String) {
def this(baz: List[Any]) = {
val bazLength = baz.length
val someText = "baz length is " ++ bazLength.toString
this(bazLength, someText)
}
}
Is it maybe a way of guaranteeing that the auxiliary constructor doesn't have side effects and/or can't return early?
Auxiliary constructors can contain more than a single invocation of another constructor, but their first statement must be said invocation.
As explained in Programming in Scala, ch. 6.7:
In Scala, every auxiliary constructor must invoke another constructor of
the same class as its first action. In other words, the first statement in every
auxiliary constructor in every Scala class will have the form this(. . . ).
The invoked constructor is either the primary constructor (as in the Rational
example), or another auxiliary constructor that comes textually before the
calling constructor. The net effect of this rule is that every constructor invocation
in Scala will end up eventually calling the primary constructor of the
class. The primary constructor is thus the single point of entry of a class.
If you’re familiar with Java, you may wonder why Scala’s rules for
constructors are a bit more restrictive than Java’s. In Java, a constructor
must either invoke another constructor of the same class, or directly invoke
a constructor of the superclass, as its first action. In a Scala class, only the
primary constructor can invoke a superclass constructor. The increased
restriction in Scala is really a design trade-off that needed to be paid in
exchange for the greater conciseness and simplicity of Scala’s constructors
compared to Java’s.
Just as in Java, one can get round this limitation by extracting the code to be executed before the primary constructor call into a separate method. In Scala it is a bit more tricky than in Java, as apparently you need to move this helper method into the companion object in order to be allowed to call it from the constructor.
Moreover, your specific case is awkward as you have two constructor parameters and - although one can return tuples from a function - this returned tuple is then not accepted as the argument list to the primary constructor. For ordinary functions, you can use tupled, but alas, this doesn't seem to work for constructors. A workaround would be to add yet another auxiliary constructor:
object Wibble {
private def init(baz: List[Any]): (Int, String) = {
val bazLength = baz.length
val someText = "baz length is " ++ bazLength.toString
println("init")
(bazLength, someText)
}
}
class Wibble(foo: Int, bar: String) {
println("Wibble wobble")
def this(t: (Int, String)) = {
this(t._1, t._2)
println("You can execute more code here")
}
def this(baz: List[Any]) = {
this(Wibble.init(baz))
println("You can also execute some code here")
}
}
This at least works, even if it is slightly complicated.
scala> val w = new Wibble(List(1, 2, 3))
init
Wibble wobble
You can execute more code here
You can also execute some code here
w: Wibble = Wibble#b6e385
Update
As #sschaef's pointed out in his comment, this can be simplified using a factory method in the companion object:
object Wobble {
def apply(baz: List[Any]): Wobble = {
val bazLength = baz.length
val someText = "baz length is " ++ bazLength.toString
println("init")
new Wobble(bazLength, someText)
}
}
class Wobble(foo: Int, bar: String) {
println("Wobble wibble")
}
Thus we need no new to create an object anymore:
scala> val w = Wobble(List(1, 2, 3))
init
Wobble wibble
w: Wobble = Wobble#47c130