To what sequence type should I default in every-day development? What type should be annotated as type ascription?
Until now I would use something like
val sequence = Seq(1, 2, 3) if I need a sequence.
But today I read that we should default to
IndexSeq(1, 2, 3) which returns an
I am now confused and wondering: What’s the right way to go?
This is a very opinion based question. So, here is my opinionated answer.
In general I do not like
Seq, both as a type and as a builder of collections, because it tells you very little about the runtime behaviour of the concrete collection; this provides an excellent discussion about that problem.
Thus, I prefer to use concrete types for most of my code. And here is my list of used types.
List as my general collection type, I use it 90% of the time. It is easy to reason about how you should use it. Also, perfect for tail-recursive functions.
ArraySeq for when I need fast index access or I am building something that will be passed to a Java method which expects an
Set when I require uniqueness and fast existence check.
Map when I need a to get value given some key.
LazyList when I need a simple lazy and maybe infinite collection. (it’s unfold method is very useful).
mutable.ListBuilder when, for performance reasons, I need to build a List imperatively.
(If you are in
Outside of the stdlib.
cats.data.NonEmptyChain when I need a non-empty collection with fast append. (usually used to collect errors).
fs2.Stream when I need a more robust and async streaming collection.
And when I want to write a generic method that accepts any collection in the stdlib. I would use
TraversableOnce to get an
BTW, I also use
Iterators internally in some function I chain a lot of transformations, to make all them lazy.
And that is it. Hope it helps.
As I said, this is just my opinion and preferences.
I recommend you to wait to see what do other people say about this. Take that, together with your own experience to form your own opinion.
Thank you so much for your answer! I know it is an opinion based questions and I am gladly thankful that you took the time to share your opinion.
Before I dug deeper into Scala’s collection framework I would use
List as my default collection type. After reading more about Scala I got the impression that it is recommended to use
IndexedSeq because their factory methods will always use the collection type which is recommended for general purposes, which could change over the time (like
Lately I read that we should use
Vector as a general purpose - and than I was not anymore sure where to go.
I hope we will able to collect some more opinions in this thread.
Also opinions –
Vector are both decent choices, but have wildly different performance characteristics. So it sort of depends on what you need to do. For problems where
List works well, I will usually default to that – as @BalmungSan says, I probably use that 90% of the time.
I sometimes use
Vector for random access, and sometimes for a collection where I need to add values other than at the front, but it’s a bit controversial.
Vector is good at everything, but it isn’t great at anything, and its performance characteristics are a little hard to describe concisely. (Many operations are often referred to as “effectively constant time”, which makes some folks quite unhappy.) So it’s great for beginners, but folks doing serious work, especially if it requires high (and well-defined) performance, tend to look elsewhere.
Seq – when I started in Scala, it was pretty common to use
Seq everywhere, and I still do so sometimes. But it loses a lot of terribly important performance information: it’s vague and mushy. So I think most of the community has moved away from using it routinely at this point, in favor of more concrete types. (Or at least, more precise traits.) I will sometimes use it as a function parameter for easy problems, but there is rarely a principled reason to use it – most problems are either flexible enough that you could use
Iterable instead, or they call for more-precise behavior, so you want one of the sub-traits.
(And the base
scala.collection.Seq is arguably slightly evil, since it allows mutable values to be passed as parameters, which is almost never what you want.)
Thank you for your thoughts!
This Part I do not quite understand: How can any collection prevent you from adding mutable objects?
scala.collection.Seq itself may be mutable. If you want an immutable Seq, use
That’s not quite what I mean. Consider: types like
Vector are immutable. You can create a new one based on an existing one, but you can never change a
Vector. That’s very powerful, and very important.
But there are mutable types in Scala’s collections as well – things like
Buffer. Since those are mutable, it means that some other thread could change them while you are in the middle of your function using them. That makes code ferociously unpredictable and non-deterministic.
So it’s usually better not to mix these up. If you ever need to use a mutable data structure, you should say so explicitly, and be clear in the functions that are calling it that this could happen. In general, I recommend avoiding using parameter types where the passed-in values could be either mutable or immutable, because it makes it hard to write reliable code.
So in general, if you are going to use
Seq, make sure it is
(This is a longtime gotcha, because plain old
scala.Seq used to allow either. This changed recently…)
As far as I understand
Seq is by default immutable due to the type alias in
That’s true, but it’s a very recent change…