-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make LazyList's empty-or-not status lazy too #7000
Conversation
This makes some of the tests non-deterministic. |
Thanks @NthPortal! I think it would be better to have |
|
||
private lazy val state: State[A] = { | ||
stateEvaluated = true | ||
val res = lazyState() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the two lines above be swapped (in case evaluating the state throws)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. stateEvaluated
is an indicator of whether or not we can touch state
without breaking laziness. IMO, even if evaluating state
throws, it should be considered evaluated and safe to touch.
|
||
lazy val head: A = { | ||
hdEvaluated = true | ||
val res = hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the two lines above be swapped (in case evaluating hd
throws)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably better to keep retrying instead of switching from an exception to nulls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Ichoran if it throws, hd
won't get set to null
, so it will keep retrying
b4dc368
to
3aebd91
Compare
I'm having trouble figuring out why |
is this only WIPpy because of the failing test, or is there other known WIPpiness here? |
@SethTisue the WIP was because I was still in the process of removing ... I have now done that, and will push it shortly. And I will get rid of the WIP label :D |
tentatively marking as an M5 blocker since if we're going make a basic change like this we need to leave folks time to test it |
e5c2060
to
5d18cca
Compare
This is ready for review |
What should we do about methods like |
JFYI |
This is a bug then, they should be implemented using the view-based approach. |
This was introduced in #6608 to stop a stack safety issue. Just my 2 cents, but I think that drop and dropWhile should still use memory sharing. perhaps something akin to
|
(introduced by me no less) |
Yes, that’s what we should do. |
Does anyone have any idea why |
From looking at the previous implementation, it used to call However in the new design the lazy State function for the filtered LazyList (for the head) holds a reference to the head of the Unfiltered LazyList. So in order for it to GC the head of the unfiltered LazyList it must first GC the lazyState function, so it could be purely that the GC path is more complicated for the JVM and it just doesn't feel like doing it 😁 Also the lazyState function wraps a byName parameter, which could also be in play here (in terms of GC). It may work locally for you due to differences in memory, cpu and jvm args. Hope that this is useful or at the very least correct 😁 |
The |
@lrytz thanks! I think I have fixed it with |
Extract Stream into its own file, and inline methods and implementation into Stream or its companion object.
Remove LazyListOps and LazyListFactory. Change LazyList to have a lazy state. The state can be either a cons cell with a lazy head, or an empty state.
bd62c88
to
5a985e9
Compare
I think everything brought up so far has been taken care of, and all the tests pass. Ready for final review so this can get in by tomorrow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a couple small comments, but I think it overall looks good and what really needs to happen is that it gets into the library so it's easier to play with it and figure out if there are any bugs and if it has the right characteristics with regard to laziness etc..
|
||
lazy val head: A = { | ||
hdEvaluated = true | ||
val res = hd() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably better to keep retrying instead of switching from an exception to nulls.
} | ||
} | ||
|
||
private class LazyIterator[+A](private[this] var lazyList: LazyList[A]) extends AbstractIterator[A] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we certain this should be private? We seem to rely on it for implementing a fair bit of functionality. How are library users supposed to replicate the functionality if this is private? If they can, why don't we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's specific to LazyList
, it's not clear to me why you'd need to replicate the functionality - you can just call .iterator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, I don't feel strongly about it either way
x = LazyList(_, ?) | ||
y = LazyList(_, ?) | ||
x = LazyList(?) | ||
y = LazyList(?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation of addString
is very hard to review. I guess all the places where you have changed the expected output of the tests are places where we previously knew that the lazy list was not empty and now we don’t know?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it's hard to review (it was also very hard to understand well enough to change to not use tailDefined
). The tests were changed either because (a) previously we knew it was not empty, but now we don't, or (b) previously the tail hadn't been evaluated to a cycle, but since the tail is no longer lazy (since the whole thing is lazy), it now knows it's cyclic slightly earlier
val stream = Iterator.iterate(Stream.from(0))(_.dropWhile(_ => false)).drop(10000).next | ||
stream.head // superfluous, because `head` should be eager | ||
stream.tail.head | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove that test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test was added in #6608, which made drop
and dropWhile
stack-safe but not lazy. Since I changed them back to being lazy, they are no longer stack-safe
@@ -101,12 +101,12 @@ class LazyListTest { | |||
|
|||
@Test // scala/bug#9134 | |||
def filter_map_properly_lazy_in_tail: Unit = { | |||
assertLazyListOpLazyInTail(_.filter(_ % 2 == 0).map(identity), List(1, 2)) | |||
assertLazyListOpLazyInTail(_.filter(_ % 2 == 0).map(identity), List(1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC this means that when we call filter
and map
we evaluate no elements of the lazy list, whereas we previously evaluated the first element?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct
Thanks a lot to all the people who contributed to that work! |
🎉 |
🎉 💯 Thanks for making this actually happen, @NthPortal! |
🎉 |
intermittent
https://scala-ci.typesafe.com/view/scala-2.13.x/job/scala-2.13.x-integrate-windows/663/consoleFull |
could it be another inlining problem? I can try |
@SethTisue is it only a Windows thing? Also, how intermittent? |
not sure yet on either question. so far I've only seen the one Windows failure, and then the next run was green |
followup discussion taking place at scala/bug#11089 on whether this is now too lazy... |
Fixes scala/collection-strawman#367