In Scala, a List[A] is an abstract class implemented using singly linked list, composed of a head element of type A, and a tail of type List[A]. The concrete implementations are Cons (::), which represents which has a head and a tail, and Nil which is the empty list.
What happens in recLast is that the List[A] is decomposed using a feature called Pattern Matching. The case denotes a pattern which we attempt to match on.
The first case:
case h :: Nil => h
Means "In case the List[A] contains a head element h and a tail which is the empty list (called Nil), then return the head element.
The second case
case b :: rest => recLast(rest)
Means "In case the List[A] contains a head element which we bind to b and a tail which we bind to rest, take the tail of the list and recursively call recLast with it.
One way of pattern matching on a List[A] is using the Cons class. By doing head :: tail, which call the ::.apply method in infix notation, it is equivalent to: ::.apply(head, tail)
Appendix
The concrete implementation of the classes:
:: (Cons):
final case class ::[B](override val head: B,
private[scala] var tl: List[B]) extends List[B] {
override def tail : List[B] = tl
override def isEmpty: Boolean = false
}
Nil:
case object Nil extends List[Nothing] {
override def isEmpty = true
override def head: Nothing =
throw new NoSuchElementException("head of empty list")
override def tail: List[Nothing] =
throw new UnsupportedOperationException("tail of empty list")
override def equals(that: Any) = that match {
case that1: scala.collection.GenSeq[_] => that1.isEmpty
case _ => false
}
}