It seems that sequence intended to return None if any element in the list is None and returns Some values โโin the list otherwise. So your intuition about the Nil case is incorrect - Nil is an empty list that does not contain None s, so the result should not be None .
Let me do it step by step, from the inside.
Suppose we have an optionList variable of type Option[List[A]] and some variable a type a . What do we get when we call:
optionList.map(a :: _)
If optionList is None , then it will be None . If optionList contains a list, say list , it will be Some(a :: list) .
Now, if for some option variable of type Option[A] , what do we get when we call:
option.flatMap(a => optionList.map(a :: _))
If option is None , then it will be None . If option contains a value, say a , then it will be optionList.map(a :: _) , which we found out above (by definition flatMap ).
Now, if we bind it together, we see that if any element is None , then the recursive call is avoided, and the whole result will be None . If no item is None , then the recursive call will continue to add item values, and the result will be Some internal values โโof the list item.
It may be clearer if you rewrite the inside:
def sequence[A](l: List[Option[A]]): Option[List[A]] = l match { case Nil => Some(Nil) case h :: t => h match { case None => None case Some(head) => sequence(t) match { case None => None case Some(list) => Some(head :: list) } } }
Or even less idiomatic, but perhaps more precise:
def sequence[A](l: List[Option[A]]): Option[List[A]] = l match { case Nil => Some(Nil) case h :: t => val restOfList = sequence(t) if (h == None || restOfList == None) None else Some(h.get :: restOfList.get) }
You can also rewrite this quite naturally as fold without recursion, in case this confuses you:
def sequence[A](l: List[Option[A]]) = (Option(List.empty[A]) /: l) { case(Some(sofar), Some(value)) => Some(value :: sofar); case(_, _) => None }