Skip to content

Commit

Permalink
Add 2.20 Must not use Seq.head rule
Browse files Browse the repository at this point in the history
  • Loading branch information
valydia authored and valydia committed Mar 15, 2018
1 parent 1ed7eb1 commit d39e39f
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ contribute.
- [2.17. SHOULD NOT define case classes nested in other classes](sections/2-language-rules.md#217-should-not-define-case-classes-nested-in-other-classes)
- [2.18. MUST NOT include classes, traits and objects inside package objects](sections/2-language-rules.md#218-must-not-include-classes-traits-and-objects-inside-package-objects)
- [2.19. SHOULD use head/tail and init/last decomposition only if they can be done in constant time and memory](sections/2-language-rules.md#219-should-use-head-tail-and-init-last-decomposition-only-if-they-can-be-done-in-constant-time-and-memory)
- [2.20. MUST NOT use `Seq.head`](sections/2-language-rules.md#210-must-not-use-seqhead)

- [3. Application Architecture](sections/3-architecture.md)
- [3.1. SHOULD NOT use the Cake pattern](sections/3-architecture.md#31-should-not-use-the-cake-pattern)
Expand Down
40 changes: 40 additions & 0 deletions sections/2-language-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,3 +705,43 @@ Unfortunately, the Scala collections library permits these kinds of inefficient
An example for an efficient init/last decomposition is `scala.collection.immutable.Queue`. It is backed by two `List`s and the efficiency of `head`, `tail`, `init` and `last` is *amortized constant* time and memory, as explained in the [Scala collection performance characteristics](http://docs.scala-lang.org/overviews/collections/performance-characteristics.html).

I don't think that init/last decomposition is all that common. In general, it is analogue to head/tail decomposition. The init/last deconstructor for any `Seq` is `:+`.


### 2.20 MUST NOT use `Seq.head`

You might be tempted to this:

```scala
val userList: List[User] = ???

// ....
val firstName = userList.head.firstName
```

Don't ever do this, as this will throw `NoSuchElementException` if the sequence is empty.

Alternatives:

1. using `Seq.headOption` possibly combined with `getOrElse` or pattern matching

Example:

```scala
val firstName = userList.headOption match {
case Some(user) => user.firstName
case _ => "Unknown"
}
```

2. using pattern matching with the cons operator `::` if you're dealing with a `List`

Example:

```scala
val firstName = userList match {
case head :: _ => head.firstName
case _ => "Unknown"
}
```

4. using `NonEmptyList` if it is required that the list should never be empty. (See [cats](https://typelevel.org/cats/datatypes/nel.html), [scalaz](https://github.com/scalaz/scalaz/blob/series/7.3.x/core/src/main/scala/scalaz/NonEmptyList.scala), ...)

0 comments on commit d39e39f

Please sign in to comment.