First, you don't need to include the list as a parameter, but you will need to use a nested function to do that.
The main issue here is that find
returns a Future[Option[Node]]
, so that means you need to work entirely "inside" futures. A Future
is a monad, which basically means two things:
- You can never directly access value inside the
Future
- You interact with the contained value by passing functions to it through
map
and flatMap
, which give you a new Future
in return.
I would suggest you read some articles about how future-based programming works, here's a decent one I found, but as a simplified example, suppose I have :
val x: Future[Int] = Future { 5 }
If I want to add 1 to the contained value, I cannot access it directly, but rather I can call map
:
val y: Future[Int] = x.map{i => i + 1}
Likewise, if I have some function like :
def addOne(i: Int): Future[Int] = //...
I can use it with my future like :
val y: Future[Int] = x.flatMap{i => addOne(i)}
Getting back to your situation, this means that getAllParents
must return a Future[List[Node]]
and we will do the recursion using flatMap
, where you create a new future every time you call find and chain them together as you traverse through them.
private def getAllParents(node: Node): Future[List[Node]] = {
def loop(node: Node, build: List[Node]): Future[List[Node]] = {
node.parentId match {
case None => Future.successful(node :: build)
case Some(parentId) => find(parentId).flatMap{parentOption => parentOption match {
case Some(parentNode) => loop(parentNode, node :: build)
case None => throw new Exception(s"couldn't find parent for $node")
}}
}
}
loop(node, Nil)
}
So you can see that loop
is sort of a recursive function, but the recursion is done inside a Future
. If the node has no parent, then we basically stick the end result directly in a new Future
and call it a day. If the node does have a parent, we then call find
, which gives us a Future[Option[Node]]
.
Now to do the recursion, we call flatMap
on this returned Future
, and we have to give it a function of type Option[Node] => Future[List[Node]]
. Now we have a function that has access to both node
and its parent, and we can add node
to the list and call loop
again on its parent.