Yatoom
Yatoom

Reputation: 317

Convert for-comprehension to LazyList

I have

I want to read the files only when we also need to calculate the statistics. The line below will loop over the filepaths, and returns its name and file contents.

for((name, path) <- filepaths) yield (name, loadData(path))

Is it possible to do such a for-comprehension for a LazyList, so that the loadData-part is evaluated lazily?

Upvotes: 1

Views: 688

Answers (2)

Boris Azanov
Boris Azanov

Reputation: 4501

for comprehension is just syntactic sugar for combinations of flatMap and map functions. You can do that you need just using map function:

def loadData(path: String): String = {
  println(s"launch loadData on $path")
  s"${path}_suffix"
}

val filePaths = Seq("p1" -> "path1", "p2" -> "path2")
val lazyList = filePaths.to(LazyList).map{
  case (name, path) => name -> loadData(path)
}
println(lazyList)
println(lazyList.force)

// output will be:
//LazyList(<not computed>)
//launch loadData on path1
//launch loadData on path2
//LazyList((p1,path1_suffix), (p2,path2_suffix))

Here we see loadData evaluates only when next element is needed.

Upvotes: 5

Levi Ramsey
Levi Ramsey

Reputation: 20611

It's most definitely possible.

for ((name, path) <- filepaths) yield (name, loadData(path))

Is just syntactic sugar for

filepaths.map { x =>
  val (name, path) = x
  (name, loadData(path)
}

(that may not be what the compiler actually desugars it to (I forget whether there would be multiple functions (one to extract the pairs and one to call loadData) or not))

Since there's a map on LazyList, if filepaths is a LazyList it will work.

That said, it's not maximally lazy: getting the nth entry requires evaluating the (n - 1)th entry.

Of course, whenever you have a Seq[(A, B)] (or Iterable[(A, B)]/ Traversable[(A, B)]), it's worth considering if what you really want is a Function1[A, B] or a Map[A, B] (which can just be thought of as an easy way to construct a subset of Function[A, B]).

// assuming that filepaths is an `Iterable[(A, B)]`
filepaths.toMap.mapValues(loadData(_))

(Note that the (_) might be able to be omitted).

This will be non-strict (it won't call loadData until you lookup a given name), but it won't be maximally lazy (there's no memoization: every lookup will result in a loadData call). If you want maximal laziness, you'd have to implement your own memoization.

Upvotes: 1

Related Questions