Idiomatic Scala: semantic difference between <Collection> Like and <Collection> Types?
As the title of this question says: my question is more about form (idiomatic convention) than about function. Put briefly:
What is the semantic difference between
MyCollectionLikeandMyCollection?
As examples: what is the difference between StringLike and String or MapLike and Map . By carefully studying the Scala API docs, I can say that XLike usually a supertype of X But, in addition, I do not understand the semantic difference between these levels of abstraction.
In practice, if I were creating a new class / attribute, understanding this difference would be helpful when I select names for the specified class.
My specific problem when this happened is as follows:
I want to create a feature:
SurjectiveMap[K, T], which can be mixed usingMap[K, Set[T]]orMapLike[K, SetLike[T]]. Given that I do not know the semantic difference between*Likeand*, I'm not sure what to go with.
Same as between IFoo and Foo, Bar and BarImpl (except for the fact that TraversableLike is a super-dash that contains an implementation):
The Scala collection library avoids code duplication and achieves the โsame resultโ principle using common builders and workarounds over collections in so-called implementation features. These named traits with the suffix Like; for example, IndexedSeqLike is an implementation tag for IndexedSeq, and in the same way, TraversableLike is an implementation trait for Traversable. Collectible classes such as Traversable or IndexedSeq inherit all their specific methods for implementing these attributes.
I think that using the Like signs allows you to specify the types of return (representation). It takes a lot more work. For comparison:
import collection.generic.CanBuildFrom object FooMap { type Coll = FooMap[_, _] implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), FooMap[A, B]] = ??? } trait FooMap[A, +B] extends Map[A, B] { def foo = 33 } def test(f: FooMap[Any, Any]) { f.map(identity).foo // nope, we ended up with a regular `Map` } against
object FooMap extends collection.generic.ImmutableMapFactory[FooMap] { override type Coll = FooMap[_, _] implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), FooMap[A, B]] = ??? def empty[A, B]: FooMap[A, B] = ??? } trait FooMap[A, +B] extends Map[A, B] with collection.immutable.MapLike[A, B, FooMap[A, B]] { def foo = 33 override def empty: FooMap[A, B] = FooMap.empty[A, B] } def test(f: FooMap[Any, Any]) { f.map(identity).foo // yes } The MapLike attribute must be mixed after the Map symbol for the correct return types.
However, you do not get everything for free, for example, you will need to override more methods:
override def +[B1 >: B](kv: (A, B1)): FooMap[A, B1] // etc.