Skip to content
This repository was archived by the owner on Dec 22, 2021. It is now read-only.

LazyList.#:: not lazy due to parser rewriting of right-associative infix operators #127

Closed
pnf opened this issue Jun 24, 2017 · 11 comments
Closed

Comments

@pnf
Copy link
Contributor

pnf commented Jun 24, 2017

Evaluating

  def bleh(i: Int) = {println(s"bleh $i"); i}
  val xs20 = bleh(1) #:: bleh(2) #:: bleh(3) #:: LazyList.empty

will immediately execute all the printlns, even though .force is never called and .evaluated remains false. If one defines a left associative prepend with the same definition as #::, then

    val x21 = LazyList.empty.prepend(bleh(3)).prepend(bleh(2)).prepend(bleh(1))

will behave in a properly lazy manner.

This is no great mystery, as the parser converts the definition of xs20 to:

 val xs20 = {   <synthetic> <artifact> val x$68 = bleh(1);
    {  <synthetic> <artifact> val x$67 = bleh(2);
  {  <synthetic> <artifact> val x$66 = bleh(3);
  LazyList.empty.$hash$colon$colon(x$66)}.$hash$colon$colon(x$67)}.$hash$colon$colon(x$68)  };

This seems to be scala/bug#7333, which was closed as a duplicate of the somewhat less general scala/bug#1980

@SethTisue
Copy link
Member

SethTisue commented Jun 24, 2017

what do you have in mind in opening this ticket, given the existence of scala/bug#1980? (I corrected your links to point to GitHub instead of JIRA)

@pnf
Copy link
Contributor Author

pnf commented Jun 24, 2017

Thank you for the correction.
Given that scala/bug#1980 has been around since 2009 and has now been relegated to the backlog., perhaps removing the #:: operator from LazyList would be a good idea? I was initially trying to add an assertion to verify laziness in lazyListTestOps, but that didn't work out so well.

@smarter
Copy link
Member

smarter commented Jun 25, 2017

FWIW, this should be much easier to fix in dotty where the problematic desugaring is not done during parsing but during typechecking, where we should have enough information to make the intermediate vals lazy. I opened scala/scala3#2808 to track this.

@pnf
Copy link
Contributor Author

pnf commented Jun 25, 2017 via email

@szeiger
Copy link
Contributor

szeiger commented Jun 26, 2017

There is a hack to make right-associative operations lazy and the current collections library already uses it. Try the same code with Stream instead of LazyList and take a look at ConsWrapper. We should be able to do the same for LazyList.

@pnf
Copy link
Contributor Author

pnf commented Jun 26, 2017

Good point. I added something similar to #128. Among other benefits, the de rigueur Fibonacci example now works without blowing stack.

@julienrf julienrf added this to the 0.3.0 milestone Jun 30, 2017
@szeiger
Copy link
Contributor

szeiger commented Jul 4, 2017

It looks like we can fix it in Scala 2.13 after all: scala/scala#5969. It requires a spec change so we should get Dotty on board, too.

@julienrf julienrf modified the milestones: 0.4.0, 0.3.0 Jul 24, 2017
@julienrf
Copy link
Contributor

julienrf commented Aug 31, 2017

Putting on hold because we have to wait for Scala 2.13.0-M3.

@julienrf julienrf modified the milestones: 0.5.0, 0.4.0 Aug 31, 2017
@optician
Copy link
Contributor

Tested with 2.13.0-M3. Works fine.

scala> def bleh(i: Int) = {println("bleh " + i); i}
bleh: (i: Int)Int

scala>   val xs20 = bleh(1) #:: bleh(2) #:: bleh(3) #:: LazyList.empty
xs20: strawman.collection.immutable.LazyList[Int] = LazyList(?)

scala> xs20.head
bleh 1
res0: Int = 1

Scala 2.13.0-M2 evaluates only first element.

@julienrf
Copy link
Contributor

Yes, see #345.

@julienrf
Copy link
Contributor

Closed by scala/scala#6509

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants