k0pernikus
k0pernikus

Reputation: 66430

How to default to immutable Seq in scala?

I learned today that scala's Seq can mean that both a mutable or immutable Seq can fulfill its type alias. (I stumbled over a blogpost talking about it.)

In my codebase, I try to follow functional programming best practices and while I did assume Seq to enforce an immutable Sequence, it may not be and hence causing an entry-point for unintended side-effects.

In order to protect against mutable Seq I could type my functions using a Sequence with:

scala.collection.immutable.Seq in each of my files in which I am expecting a Seq.

I also do not prefer to read immutable.Seq in my code base.

For me, Seq should behave just like Set alias and be immutable by default.

Is there a way I can make the default Seq alias to point to its immutable counterpart for my project without hindering vendor imports?

Upvotes: 2

Views: 5119

Answers (2)

jwvh
jwvh

Reputation: 51271

OK, having read the (4-year old) blog post, I really don't see this as being much of an issue.

The default Seq() is collection.Seq which isn't mutable itself, but it is the parent of both the mutable and immutable variants. So code that creates and passes default Seq collections isn't effected but code that accepts a Seq parameter might receive a mutable.Seq. But that's only an issue if immutability is not just assumed but required, e.g. for thread safety.

Bottom line: Put an import collection.immutable.Seq at the top of those files where:

  1. Client code that you don't control might send you a mutable Seq, and
  2. The immutability of that received collection is of paramount importance.

Upvotes: 3

user9559692
user9559692

Reputation:

I wouldn't recommend trying to mess with default Scala behaviour. Unless you define your own function which takes any input and returns an immutable Seq of that input, I think it'd be safest to just use immutable.Seq

You can always try something like this (not thoroughly tested):

def seq(inputs: Any*): scala.collection.immutable.Seq[Any] = {
  scala.collection.immutable.Seq(inputs)
}

scala> seq("hi", true)
res0: scala.collection.immutable.Seq[Any] = List(WrappedArray(hi, true))

scala> seq(1, 'c', "string", false)
res1: scala.collection.immutable.Seq[Any] = List(WrappedArray(1, c, string, false))

Edit:

As jwvh pointed out, this option isn't ideal. Using generic types like James Whiteley suggested could be better if you insist on making your own function:

def seq[T](inputs: T*): scala.collection.immutable.Seq[T] =
  scala.collection.immutable.Seq(inputs).flatten

This outputs:

scala> seq("hi", true)
res0: scala.collection.immutable.Seq[Any] = List(hi, true)

But I'll reiterate - it might just be best to use immutable.Seq where needed, or find another collection type to use.

Upvotes: 1

Related Questions