Reputation: 8043
In the book Functional Programming in Scala there is a question that asks to convert a List of Options into an Option of Lists. The function signature is as follows:
def sequence[A](a:List[Option[A]]):Option[List[A]]
On the book's website, this function is implemented as follows:
def sequence[A](a:List[Option[A]]):Option[List[A]] = a match {
case Nil => Some(Nil)
case h::t => h flatMap (r => sequence(t) map (h::_))
}
I am a little confused by the h flatMap (r => sequence(t) map (h::_))
part. If I break it down, it goes as follows:
h is of type Option[A]
. Performing flatMap on h would return an Option[B]
but it takes as a parameter a function f that takes A as a parameter and return an Option[B]
, now in the example above, sequence(t) map (h :: _)
would return an Option[List[A]]
which aligns with the return type of the function.
Can map be used instead of flatMap to perform the transformation from List[Option[A]]
to Option[List[A]]
? Also, the solution provided doesn't seem to be tail-recursive. Can it be made tail-recursive?
Upvotes: 3
Views: 1756
Reputation: 503
I found another way to implement the seq function that is not using recursion implicitely:
def seq[A](a: List[Option[A]]):Option[List[A]] = a.foldLeft(Some(Nil):Option[List[A]])((collected,elem) => elem.flatMap(el=> collected.map(el::_)))
Upvotes: 1
Reputation: 31724
h flatMap (r => sequence(t) map (h::_))
should be h flatMap (r => sequence(t) map (r::_))
as h
is of type Option[A]
and r
is of type A
. We are trying to append element to the list of type List[A]
in the map and hence r::_
.
Another solution without using recursion would be:
def sequence[A](a: List[Option[A]])(implicit nullValue: A):Option[List[A]] = {
Option(a map { x => x.getOrElse(nullValue)})
}
Upvotes: 1
Reputation: 38045
There is a typo:
case h::t => h flatMap (r => sequence(t) map (h::_))
There should be r :: _
, or h.toList ::: _
, not h :: _
sequence(t)
returns Option[List[A]]
. map (r::_)
on Option[List[A]]
don't change the type. It just takes a List[A]
from Option
if any and prepends it with A
.
So the type of sequence(t) map (r :: _)
is Option[List[A]]
.
You don't need flatMap
here:
def sequence[A](a:List[Option[A]]):Option[List[A]] = a match {
case Nil => Some(Nil)
case None :: _ => None
case Some(r) :: t => sequence(t) map (r :: _)
}
The solution can be made tail-recursive. The common problem with tail-recursion and List
is that you have to reverse you list in the end:
def sequence[A](a:List[Option[A]]):Option[List[A]] = {
@tailrec def loop(a: List[Option[A]], subres: List[A] = Nil): Option[List[A]] =
a match {
case Nil => Some(subres)
case None :: _ => None
case Some(r) :: t => loop(t, r :: subres)
}
loop(a) map {_.reverse}
}
In fact it can be made not recursive at all:
def sequence[A](a:List[Option[A]]):Option[List[A]] =
a.foldLeft(Option(List[A]())){ (os, oe) =>
for {
s <- os
e <- oe
} yield e :: s
}.map{ _.reverse }
Upvotes: 4