Looking at the scaladoc for Traversable and TraversableLike, I'm having a hard time figuring out what the difference between them is (except that one extends the other). The only apparent difference in the documentation is that it says Traversable is a "trait" and TraversableLike is a "template trait". But googling for "template trait" does not reveal a definition for this term. Help!
I haven't seen this terminology in general use in Scala, and I think it's specific to the design of the Scala collections API. You can learn more by reading The Architecture of Scala Collections (especially the section on "factoring out common operations")[1] and the Scala collections SID. §4.2 of the SID is relevant, although they're referred to as "implementation traits" there:
Collection classes such as Traversable or Vector inherit all their concrete method implementations from an implementation trait. These traits are named with the Like suffix; for instance VectorLike is the implementation trait for Vector and TraversableLike is the implementation trait for Traversable.
In short, their purpose is both to separate implementation for use outside the collections hierarchy (e.g. StringOps extends TraversableLike but not Traversable) and to factor out common operations in such a way that the collection type is preserved (see IttayD's answer for a more thorough explanation).
I should note that you really don't need to be concerned with these classes unless you are extending the collections hierarchy. For ordinary use, focus on the Traversable, Iterable, Seq, etc. traits. If you're new to the Scala Collections API, I would suggest starting with the Scala 2.8 Collection API document, then reference the scaladoc as necessary. You can't expect to get the 'big picture' looking through the scaladoc.
[1] credit goes to michid for this link
The XXXLike traits have an important role of adding the Repr generic parameter. Methods that are supposed to return the same collection type, like filter, map, flatMap, are implemented in low level traits (TraversableLike). To encode their return type, those traits receive it:
trait TraversableLike[+A, +Repr] ...
...
def filter(p: A => Boolean): Repr = {
(for map and flatMap the issue is more complicated, I won't go into it here)
Now say you have a new type of collection. You could do:
trait MyCollection[+A] extends TraversableLike[A, MyCollection]
But then if anyone wants to extend your collection, they are stuck with return values of MyCollection from the various inherited methods.
So instead, you create:
trait MyCollectionLike[+A, +Repr] extends TraversableLike[A, Repr]
and
trait MyCollection[+A] extends MyCollectionLike[A, MyCollection]
and anyone that wants to extend your collection extends MyCollectionLike
The [...]Like classes are implementation classes for the actual collection classes. In a sense they act like template implementations from which most - if not all - behavior is inherited by the actual collection classes.
For a very detailed and approachable overview read The Architecture of Scala Collections.
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.
I'm new to Scala, coming from Java, and I was just reading about traits. One thing that gets mentioned often is that traits don't (can't? won't?) have constructor parameters. I was curious to know if there was a reason for this.
Coming from a long ago maths/computer-science background I was was wondering if this was an inevitable consequence because of some language design decision, or if it was a conscious decision to avoid some inheritance/mix-in problem or another?
Was hoping someone might know because it feels like there might be something interesting behind the fact.
The other answers describe the language; I suspect your question may really be "why is it designed in this way".
I believe it arises out of the awkwardnesses and verboseness that would arise when extending multiple traits, especially with overrides and with types, and various mix-in strategies.
The Cake Pattern often results in various traits providing missing bits to each other in a way that is totally invisible - by design - in the mixing class. And mixing can be bi-directional, using self-types. So the construction of a class from traits can be a very messy business for the compiler. Scala often trades simplicity of compiler design and implementation for simplicity of language use and code reduction, and this is certainly a good example.
So while there may be simple, hierarchical cases where having a constructor might be useful and sufficient, it would almost certainly have to be redundant of other mechanisms for more difficult, non-hierarchical scenarios.
Scala 3 will allow trait parameters. Here's a sample from the docs
trait Greeting(val name: String) {
def msg = s"How are you, $name"
}
class C extends Greeting("Bob") {
println(msg)
}
The answer is: that's what Scala is right now.
But that might not be the case in the future: trait parameters can replace early initializers. (see Martin Odersky's recent Scala Days presentation page 34)
Scala: Where It Came From & Where It is Going
Traits don't have constructor parameters because traits cannot be constructed. Given any trait T it's not possible to instantiate any object of type exactly T. You can override trait defs with vals though, so
trait Foo {
def bar: String
}
class Baz(override val bar: String) extends Foo
You can't construct them directly because new MyTrait {} is actually sugar for an anonymous class of new Object with MyTrait {}
Trait is analog for Java Interface. The main difference is that trait can have default implementation for their methods.
So Java interfaces can't have constructor so do Scala traits
Scala 3 allows traits with parameters, just like classes have parameters.
I'm reading the first section of the book "Scala in depth". In the first section, it gives an example which convert a java JdbcTemplate interface to scala:
Java code:
public interface JdbcTemplate {
List query(PreparedStatementCreator psc,
RowMapper rowMapper)
}
Scala code:
trait JdbcTemplate {
def query[ResultItem](psc : Connection => PreparedStatement,
rowMapper : (ResultSet, Int) => ResultItem) : List[ResultItem]
}
Then it says:
With a few simple transformations, we've created an interface that works directly against functions. This is a more functional approach simply because scala's function traits allow composition. By the time you're finished reading this book, you'll be able to approach the design of this interface completely differently.
I can't understand "traits allow composition" here, since I can't find any "composition" in the example provided.
Do I miss anything?
You've missed the first part of this phrase:
scala's function traits allow composition
In scala function is a simple object constructed from a trait like Function[-T, +R] and on all such traits you have two methods: andThen and compose.
Your code snippet is not a compositon sample but would allow for composition. Indeed, a trait alone composed nothing more than..itself with itself :)
Trait allows to compose at runtime thanks to the withkeyword.
Basically, it allows to add trait's methods to associated trait or class or even...runtime object!
Composition is known to be a runtime feature (its major benefit) and inheritance is known to be a compilation feature, thus static.
Here's a sample using trait composition at runtime:
val obj1 = new MyClass() with JdbcTemplate
//obj1.query .......
Compared to Java, if MySuperClass was a class or abstract class, you could still add/alter behaviour from elsewhere at runtime by implementing some kind of Strategy pattern etc... but it would require you to describe an interface + implementation..boring.
On the contrary, Trait allows to define method in one place.
That's why in scala, trait is said to "allow composition", and obviously, functional programming concept fit better with composition rather than inheritance.
UPDATE ----------------------
#Alexlv is right => the important word was Scala's FUNCTION traits.
My above explanation still right, but doesn't correspond to what the author wanted to mean.
IMHO, he wanted to mean: Connection => PreparedStatement is a Function1, and as functions are fond of composition, it enables some nice processing providing from the functional programming word to generate the required query method parameter.
The point the author is making is that replacing PreparedStatementCreator and RowMapper with function types yields a more functional API (surprise!). The API better supports code reuse since the psc and rowMapper functions can be composed from simple functional components. You might have functions, for example, that filter the ResultSet, extract various pieces of data, transform the data (map), aggregate it (fold), etc.
As #Alexiv pointed out, all Scala function objects (via the various Function traits) provide andThen and compose methods that support function composition.
As far as I understand value classes in Scala are just there to wrap primitive types like Int or Boolean into another type without introducing additional memory usage. So they are basically used as a lightweight alternative to ordinary classes.
That reminds me of Haskell's newtype notation which is also used to wrap existing types in new ones, thus introducing a new interface to some data without consuming additional space (to see the similarity of both languages consider for instance the restriction to one "constructor" with one field both in Haskell and in Scala).
What I am wondering is why the concept of introducing new types that get inlined by the compiler is not generalized to Haskell's approach of having zero-overhead type wrappers for any kind of type. Why did the Scala guys stick to primitive types (aka AnyVal) here?
Or is there already a way in Scala to also define such wrappers for Scala.AnyRef types?
They're not limited to AnyVal.
implicit class RichOptionPair[A,B](val o: Option[(A,B)]) extends AnyVal {
def ofold[C](f: (A,B) => C) = o map { case (a,b) => f(a,b) }
}
scala> Some("fish",5).ofold(_ * _)
res0: Option[String] = Some(fishfishfishfishfish)
There are various limitations on value classes that make them act like lightweight wrappers, but only being able to wrap primitives is not one of them.
The reasoning is documented as Scala Improvement Process (SIP)-15. As Alexey Romanov pointed out in his comment, the idea was to look for an expression using existing keywords that would allow the compiler to determine this situation.
In order for the compiler to perform the inlining, several constraints apply, such as the wrapping class being "ephemeral" (no field or object members, constructor body etc.). Your suggestion of automatically generating inlining classes has at least two problems:
The compiler would need to go through the whole list of constraints for each class. And because the status as value class is implicit, it may flip by adding members to the class at a later point, breaking binary compatibility
More constraints are added by the compiler, e.g. the value class becomes final prohibiting inheritance. So you would have to add these constraints to any class who want to be inlineable that way, and then you gain nothing but extra verbosity.
One could think of other hypothetical constructs, e.g. val class Meter(underlying: Double) { ... }, but the advantage of extends AnyVal IMO is that no syntactic extensions are needed. Also all primitive types are extending AnyVal, so there is a nice analogy (no reference, no inheritance, effective representation etc.)
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.