General documentation / cheat sheets for various languages and services


sequence operators

:: prepends a single element to a list. E.g.: 1 :: List(2, 3, 4) produces List(1, 2, 3, 4)
+: generalized :: introduced in Scala 2.10. E.g., val head +: tail = List(1, 2, 3)
:+ backwards +:. E.g., val tail :+ head = List(1, 2, 3)
++ concatenates two lists. E.g.: List(1, 2) ++ List(3, 4) produces List(1, 2, 3, 4).
::: concatenates two lists. E.g.: List(1, 2) ::: List(3, 4) (which is interpreted as List(3, 4).:::List(1, 2)) produces List(1, 2, 3, 4). So, effectively, it's equivalent to ++.

misc operators

@ is used in pattern-matching, where you want to capture the entire match as a variable. For example, if you want the head as well as the whole list, so that you can repeat the head at the beginning: List(List(2, 3, 4), List(4, 9, 16)).map { case (lst @ head :: _) => head :: lst } Produces: List(List(2, 2, 3, 4), List(4, 4, 9, 16))
^^ connects a Parser match to a post-processing step. For example, """\d+(\.\d*)?""".r ^^ { _.toDouble } parses the regex match as a Double.

type operators

<% view bound; specifies adherence to a trait (type class). A <% Ordered[A], for example, says that the type parameter you're using, A, must implement the Ordered trait, i.e., it must be viewable as an Ordered[A].
<: upper type bound; T <: A requires that T is a subtype of A.
>: lower type bound; B >: A requires that B is a supertype of A.

List[+A]#sum is declared as def sum[B >: A](implicit num: Numeric[B]): B, which says that the returned value B will be a supertype of the list items. The implicit bit is evidence that the returned value will be numeric, which implies that the list's items of type A must be a subtype of Numeric[B]. This is more complicated than it really ought to be (def sum[A <: Number]: Number) since Scala's numeric types (e.g., Int, Double), don't actually inherit a common type ancestor like Number.

=:= generalized type constraint; A =:= B (a.k.a., =:=[A, B]) specifies that the type A must exactly equal the type B. Inside a trait/class/whatever with some parameterized type a: A, we could define a method def squared(implicit evidence: A =:= Int) = a * a, and then call my_a.squared that would only compile (type check) if A is precisely Int.
<:< generalized type constraint; A <:< B (a.k.a., <:<[A, B]) specifies that A must be a subtype of B, similar to <:.
<%< generalized type constraint; A <%< B (a.k.a., <%<[A, B]) specifies that A must be viewable as B, similar to <% (via implicit conversion).

covariance [+A] and contravariance [-A]

co(ntra)variance is more a property of inheritance than an immediate property of the specific type it is declared on. Suppose we have:

class Animal class Mammal extends Animal class Reptile extends Animal

The covariance annotation on collection.immutable.List[+A] means that we can treat the items of a List[Mammal] as instances of Animal. If we add an Animal to a List[Mammal], it would return a new List[Animal]. We could also add a Reptile and have the same effect.

On the other hand, mutable collections like collection.mutable.ListBuffer[A] are not covariant; if they were, we could add an Animal to an existing ListBuffer[Mammal], which is problematic. The collection is mutable, but its type (parameters) are not, so there has to be a stricter restriction on what type of elements the collection can contain.

The contravariance annotation -A in Function1[-A,+B] (a function that takes an A and returns a B) means that wherever we need a function of this type, we can alternatively supply a function that converts A or some supertype of A, to B or some subtype of B.

The following example demonstrates contravariance (not covariance, but covariance can also come into play):

val drawAnimal: Animal => Unit = { animal => println("o~") } val drawMammal: Mammal => Unit = { mammal => println(":°:") } val initCanvas = { () => println("---") } def MammalCanvas(drawFn: Mammal => Unit, mammal: Mammal) = { initCanvas(); drawFn(mammal) }

Now we can call the MammalCanvas function with drawMammal, which doesn't use contravariance:

MammalCanvas(drawMammal, new Mammal) --- :°:

Or, since drawFn has the type Function1[-Mammal,+Unit], not Function1[Mammal,+Unit], we can use contravariance to call it with drawAnimal:

MammalCanvas(drawAnimal, new Mammal) --- o~



The Scaladoc on performance characteristics is nice.

Iterable is the most basic trait of an ordered collection. (It inherits Traversable, but Traversable also applies to unordered types, like Set and plain HashMap.) But it doesn't necessarily tell you the size, let you index, or iterate the collection more than once.
Seq is the basic trait for indexable ordered collections.

Instantiating a Seq creates a LinearSeq, i.e., a List.

IndexedSeq is the first of the two principle subtraits of Seq, best suited for random access to elements and determining the size of the sequence.

Vector is the primary implementation. Range and WrappedString are special cases of IndexedSeq, and thus presumably backed by Vector instances.

LinearSeq is the second of the two principle subtraits of Seq, better suited for head and tail access.

List is the primary implementation. Stream, Queue, and Stack are other collections based on LinearSeq (Stack has been deprecated).

List is the most basic actual implementation of LinearSeq, and it's the default implementation of Traversable, Iterable, Seq, LinearSeq, and of course List, meaning you can call any of those as a function, and they'll all give you back a List.

(Unless you're in mutable-land, in which case Traversable, Iterable, and Seq will all return an ArrayBuffer, while LinearSeq will return a MutableList. There is no mutable.List.)

Apparently List is most similar to Java's LinkedList.

Vector is the most basic actual implementation of IndexedSeq, and it's the default implementation of IndexedSeq and Vector, meaning you can call either of those as a function, and they'll all give you back a Vector.

(In mutable-land, IndexedSeq returns an ArrayBuffer instead.)

Vector is preferable to List if you're going to be parallelizing over the collection.

Stream derives from LinearSeq, but it's not really a good name, since it memoizes everything that gets evaluated, instead of discarding it. What I generally think of when I hear Stream is closer to the Iterable level — I think LazyList would be a better name.
mutable.ArrayBuffer is the Scala equivalent of Java's ArrayList. It sort of derives from IndexedSeq, and it's the mutable version of Vector. It's best for random access and random writes, less good at prepending and appending.
mutable.Queue is backed by mutable.MutableList.
mutable.ListBuffer seems to be the mutable version of List. It's best for prepend and append operations, less suited for random access or random writes. I'm not sure how it differs from mutable.Queue.