1

Just for testing purposes I wanted to compute lazily 2 elements:

  Stream(
    { Thread.sleep(2000); 1 },
    { Thread.sleep(2000); 2 },
    Stream.empty[Int]
  ).foreach(println)

But running this code does not yield the desired result. The values seem to show up all at the same time in the output. The reason is that the Stream() constructor is taking an array that has to be computed eagerly. So to fix that, I had to resort to manually creating the thunks of the stream like this:

  (
    { Thread.sleep(2000); 1 } #::
    { Thread.sleep(2000); 2 } #::
    Stream.empty[Int]
  ).foreach(println)

which now works exactly as intended but is not particularly pretty.

Is there a cleaner and more convenient way of having something syntactically similar to Stream(a, b, c) but that evaluates the arguments lazily?

Thanks

devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • It probably should be *syntactically similar* but "*semantically **not** equivalent*", because it's exactly the different semantics that you are after. – Andrey Tyukin Apr 07 '18 at 12:30

2 Answers2

2

If you create a special class that can hold by-name blocks that produce A together with a short helper method that helps instantiate this class:

class ByNameArg[+A](a: => A) {
  def unpack: A = a
}

/** Single lazy by-name argument wrapper */
def l[A](a: => A) = new ByNameArg(a)

then you can define the following factory method:

def lazyStreamInit[A](xs: ByNameArg[A]*): Stream[A] = {
  if (xs.isEmpty) {
    Stream.empty[A]
  } else {
    xs.head.unpack #:: lazyStreamInit(xs.tail: _*)
  }
}

It can be used with little syntactic overhead (you have just to prepend a single character l to each block that you pass in the vararg-list):

lazyStreamInit(
  l{ Thread.sleep(2000); 1 },
  l{ Thread.sleep(2000); 2 }
)

In contrast to usual () => A-workaround, this directly produces a Stream[A], not a Stream[() => A].

Getting rid of the l or { () => ... } does not seem possible, because repeated by-name arguments are not supported.


Edit

By "the usual () => A-workaround" I meant something like

Stream(
  { () => Thread.sleep(2000); 1 },
  { () => Thread.sleep(2000); 2 }
).map{ _() }.foreach(println)

Note that you have to append an additional map-step to make it a Stream[A].

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
1

This would require repeated by-name parameters which aren't currently supported (http://docs.scala-lang.org/sips/repeated-byname.html, https://github.com/scala/bug/issues/5787). They are available in Dotty, though.

You could use repeated () => A, but then you'd need to write

def makeStream[A](xs: (() => A)*) = ...

makeStream(
  { () => Thread.sleep(2000); 1 },
  { () => Thread.sleep(2000); 2 }
).foreach(println)
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487