4

What is the reasoning behind making :: specific to List and not available to all subclasses of Seq? To give a concrete example:

// :: for pattern matching
def getTail[E](s: Seq[E]): Seq[E] = s match {
   case head :: tail => tail
   case empty => empty
}
getTail(Seq(1, 2)) // returns List(2) as expected
getTail(Seq()) // returns List() as expected
getTail(Queue(1, 2)) // returns Queue(1, 2), not Queue(2)
getTail(Buffer(1, 2)) // returns ArrayBuffer(1, 2), not ArrayBuffer(2)

// :: for building sequences
1 :: 2 :: 3:: Nil // Creates List(1, 2, 3)
1 :: 2 :: List(3) // same as above
1 :: 2 :: Queue(3) // does not compile. :: is not a method within Queue

All sequences are ordered and have a notion of "head" and "tail," so why did the implementors of the collections library only provide :: to List? Why must I use +: if I want to be able to work with all Seq?

Edit: I understand the performance argument - I've been given it nearly every time I've asked this question - but I largely disagree with it. Imagine a different collection library where only one thing has been changed: the removal of :: from List. What would change?

  • I can still build my lists in a performant way using +: and Nil such as "A" +: "B" +: Nil.
  • I can still elegantly pattern match using +: to extract different parts of my list. e.g. case head +: tail => println(head)
  • There is a unified method for building and pattern matching all subclasses of Seq. This removes the need for a linter to find subtle bugs like the one I've introduced in getTail.

I honestly do not see how a method unique to List makes anything easier, clearer, or more performant than using +:. And if that is the case, then I see no reason for :: to be specific to List. Given this, I don't see the reason for :: to exist at all.

Jake Greene
  • 5,539
  • 2
  • 22
  • 26
  • 1
    it's confusing enough that they need lint rules to guard against it. But not every seq has an efficient tail (if allocations are required). – som-snytt Oct 23 '15 at 18:53
  • I think it's there so the :: unapply / extractor can be efficient. That would mean :: (as cons) is there just for symmetry. – The Archetypal Paul Oct 26 '15 at 14:28
  • @TheArchetypalPaul It is trivial to make the `::` case class work with all `Seq` instead of just `List`. The extractor would still be efficient for `List`, would it not? – Jake Greene Oct 26 '15 at 15:12

1 Answers1

3

The same reasoning as behind all class-specific operators. Like #:: for Stream or ! for Actor.

As List is single-linked, prepending elements and deconstruction into head + tail is performed in O(1). It's basically the most used operation for Lists. The whole idea of List is built around this operation.

Please note, that List supports Seq's +: as well. But using :: clearly shows your intentions to use List in efficient manner.

Aivean
  • 10,692
  • 25
  • 39
  • I've responded to this by editing my question. I don't believe performance is a valid reason for having `::`, as `+:` serves an identical purpose, is just as performant, and is usable by all subclasses of `Seq`. – Jake Greene Oct 25 '15 at 02:05