I'm new to scala(just start learning it), but have figured out smth strange for me: there are classes Array and List, they both have such methods/functions as foreach, forall, map etc. But any of these methods aren't inherited from some special class(trait). From java perspective if Array and List provide some contract, that contract have to be declared in interface and partially implemented in abstract classes. Why do in scala each type(Array and List) declares own set of methods? Why do not they have some common type?
But any of these methods aren't inherited from some special class(trait)
That simply not true.
If you open scaladoc and lookup say .map method of Array and List and then click on it you'll see where it is defined:
For list:
For array:
See also info about Traversable and Iterable both of which define most of the contracts in scala collections (but some collections may re-implement methods defined in Traversable/Iterable, e.g. for efficiency).
You may also want to look at relations between collections (scroll to the two diagrams) in general.
I'll extend om-nom-nom answer here.
Scala doesn't have an Array -- that's Java Array, and Java Array doesn't implement any interface. In fact, it isn't even a proper class, if I'm not mistaken, and it certainly is implemented through special mechanisms at the bytecode level.
On Scala, however, everything is a class -- an Int (Java's int) is a class, and so is Array. But in these cases, where the actual class comes from Java, Scala is limited by the type hierarchy provided by Java.
Now, going back to foreach, map, etc, they are not methods present in Java. However, Scala allows one to add implicit conversions from one class to another, and, through that mechanism, add methods. When you call arr.foreach(println), what is really done is Predef.refArrayOps(arr).foreach(println), which means foreach belongs to the ArrayOps class -- as you can see in the scaladoc documentation.
Related
I'm looking at Scala Collections implementation in 2.12 and have some question about the design. There are some "abstractions types" used in there:
$Coll$Like
Gen$Coll$Like
Gen$Coll$
As far as I understand the Gen$Coll$Like traits exist solely for purpose of reusing declarations (not definitions), i.e. all methods in Gen$Coll$Like traits are abstract.
$Coll$Like in turn declares a few abstract methods and implement the rest of the Gen$Coll$Like methods through them. Besides that $Coll$Like-traits have additional type parameter +Repr which I believe is for cases like SeqLike[Char, String] so we can look at String as Seq[Char].
The most questionable traits are Gen$Coll$. I do not really understand the point of them. In the hierarchy of Seq all Gen$Coll$ traits are empty till GenTraversableOnce (also, I expected GenTraversableOnce to be named GenTraversableOnceLike since it provides abstract method declarations only):
GenSeq <--- GenIterable <--- GenTraversable <--- GenTraversableOnce
Can you explain the purpose of Gen$Coll$ traits? The example of where
Gen$Coll$ actually useful (for any purpose) would be highly appreciate.
There are many classes which inherit trait Set.
HashSet, TreeSet, etc.
And there's Object(could i call it companion object of trait Set? not in the case of class Set?) Set and trait Set.
It seems to me that just adding one more class "Set" to this list make it seems to be really easy to understand the structure.
is there any reason Class Set should not exists?
If you just need a set, use Set.apply and you will have a valid set that supports all important operations. You don't need to worry how is it implemented. It is prepared to work well for most use cases.
On the other hand, if performance of certain operations matters for you, create a concrete class for concrete implementation of set, and you will know exactly what you have.
In java you would write:
Set<String> strings = new HashSet<>(Arrays.asList("a", "b"));
in scala you could as well have those types
val strings: Set[String] = HashSet("a", "b")
but you can also use a handy factory if you don't need to worry about the type and simply use
val strings = Set("a", "b")
and nothing is wrong with this, and I don't see how adding another class would help at all. It is normal thing to have an interface/trait and concrete implementations, nothing in the middle is needed nor helpful.
Set.apply is a factory for sets. You can check what is the actual class of resulting object using getClass. This factory creates special, optimized sets for sizes 0-4, for example
scala.collection.immutable.Set$EmptySet$
scala.collection.immutable.Set$Set1
scala.collection.immutable.Set$Set2
for bigger sets it is a hash set, namely scala.collection.immutable.HashSet$HashTrieSet.
In Scala there is no overlap between classes and traits. Classes are implementations that can be instantiated, while traits are independently mixable interfaces. The use of Set.apply gives an object with interface Set and that is all you need to know to use it. I fully understand wanting a concrete type, but that would be unnecessary. The right thing to do here is save it to a val of type Set and use only the interface Set provides.
I know that may not be satisfying, but give it time and the Scala type system will make sense in terms of itself, even if that is different than what Java does.
Could someone please help me understand Scala's various "Like" traits in the collection API. I've been reading over and trying to compare each without luck. I think I can see that Map for example, extends MapLike - adding 2 concrete methods. But this begs the question of why do this at all? Why not just have 1 Map trait in the Collections API instead of Map and MapLike?
Thank you!
The best source for these details is Martin Odersky and Lex Spoon's "What's New in Scala 2.8: The Architecture of Scala Collections":
The Scala collection library avoids code duplication and achieves the
"same-result-type" principle by using generic builders and traversals
over collections in so-called implementation traits. These traits
are named with a Like suffix; for instance, IndexedSeqLike is the
implementation trait for IndexedSeq, and similarly,
TraversableLike is the implementation trait for Traversable.
Collection classes such as Traversable or IndexedSeq inherit all
their concrete method implementations from these traits.
Implementation traits have two type parameters instead of one for
normal collections. They parameterize not only over the collection's
element type, but also over the collection's representation type,
i.e., the type of the underlying collection, such as Seq[I] or List[T]...
The whole article is extremely useful if you want to integrate your own collection classes with the Collections API, or if you just want a deeper understanding of how the library works.
Scala has both a mutable and an immutable Map ,
but it has only an immutable List.
If you want a mutable List you need a ListBuffer.
I don't understand why this is so.
Any one knows?.
You can choose between these:
scala.collection.mutable.DoubleLinkedList
scala.collection.mutable.LinkedList
scala.collection.mutable.ListBuffer
scala.collection.mutable.MutableList
So, yes, Scala has mutable lists :-)
I hope that this article may be of some use to you. The diagram at the bottom of the page is particularly useful in providing the mutable and immutable classes.
http://www.scala-lang.org/docu/files/collections-api/collections_1.html
There is a mutable List, but it is called Buffer. The article linked by Graham goes into more depth, but I thought there should be a specific answer to the question as well.
Map is a trait -- like Java's interface --, while List is a class, a concrete implementation of a Seq. There are mutable and immutable Seq, just like for Map.
This may be confusing to Java programmers because, in Java, List is an interface, whose (main) implementations are ArrayList and LinkedList. Alas, Java naming is atrocious. First, ArrayList is not a List by any stretch of imagination. Also, the interface has methods that are not really related to any traditional list.
So, if you want mutable/immutable equivalence, look to concrete subclass implementations of Seq.
Trait Traversable has methods such as toList, toMap, ToSeq. Given that List, Map, Seq are subclasses of Traversable, this creates a circular dependency, which is generally not a desirable design pattern.
I understand that this is constrained to the collections library and it provides some nice transformation methods.
Was there any alternative design considered? Such as a "utility" class, or adding the conversion methods to Predef?
Say I want to add a new class: class RandomList extends List {...}. It would be nice to have a method toRandomList available for all Traversable classes, but for that I would need to "pimp my library" with an implicit on Traversable? This seems a bit of an overkill. With a utility class design, I could just extend that class (or Predef) to add my conversion method. What would be the recommended design here?
An alternative and extensible approach would be to[List], to[RandomList].
It's a bit tricky to add this with implicits, though. https://gist.github.com/445874/2a4b0bb0bde29485fec1ad1a5bbf968df80f2905
To add a toRandomClass you'd have to resort to a pimp my library pattern indeed. However, why do you think that is overkill? The overhead is negligible. And it wouldn't work extending an utility class -- why would Scala look into your new class for that method? Not to mention that you'd have to instantiate such a class to be able to access its methods.
There is no circular dependency here.
Circular dependency is what happens when there are a few independent components which refer to each other.
Scala standard library is one component. Since it is built always in one step there is no problem.
You are right. Let's remove toString from the String class...