Skip to content
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 interleave logarithmtic rather than linear #313

Merged
merged 1 commit into from
Jan 23, 2020

Conversation

edsko
Copy link
Contributor

@edsko edsko commented Aug 9, 2019

The version of interleave before this PR was closely modelled on my
blog post; a tiny bit too closely; it wasn't the intention of the blog
post to define "good" shrinkers. I had a footnote for that but should
have made it a bit more prominent. Anyway, in this PR I change
interleave from being linear (removing one element at a time) to
logarithmic (trying to remove as big a chunk as possible at a time,
trying the whole list, half the list, etc.), matching the behaviour of
QuickCheck's shrinkList. This can have rather dramatic consequences
for the performance of shrinking.

The version of `interleave` before this PR was closely modelled on my
blog post; a tiny bit _too_ closely; it wasn't the intention of the blog
post to define "good" shrinkers. I had a footnote for that but should
have made it a bit more prominent. Anyway, in this PR I change
`interleave` from being linear (removing one element at a time) to
logarithmic (trying to remove as big a chunk as possible at a time,
trying the whole list, half the list, etc.), matching the behaviour of
QuickCheck's `shrinkList`. This can have rather dramatic consequences
for the performance of shrinking.
(xs, _y, zs) <- splits ts
pure . TreeT . pure $
interleave (xs ++ zs)
-- | @removes n@ computes all ways we can remove chunks of size @n@ from a list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what this is doing, but not quite sure it's clear from this sentence!

'all the ways we can remove chunks of size n' sounds like you'd have:

-- > removes 2 [1..4] = [[3,4],[1,4],[1,2]]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. Suggested rewording?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would "Splits a list into chunks of size n and returns all possible lists where one of these chunks has been removed" be less confusing?

Copy link
Member

@jacobstanley jacobstanley Aug 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't mind the behavior, but I can't reconcile how chunks of size 2 are removed from:
-- > removes 2 [1..5] == [[3,4,5],[1,2,5],[1,2,3,4]]

Ah ok, that is this part yes?

Note that the last chunk we delete might have fewer elements than n.

Ok, I think I get it now, posting this ramble for posterity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it removed the chunk 5 : nothingMoreToRemove :)

--
-- Note that the last chunk we delete might have fewer elements than @n@.
removes :: forall a. Int -> [a] -> [[a]]
removes k = \xs -> go xs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er, this should be eta reduced of course; this is a remnant of an earlier version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to do this? Happy with the rest

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
removes k = \xs -> go xs
removes k = go

Copy link
Member

@jacobstanley jacobstanley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow sorry, I made some comments but they were stuck in pending review, sorry they were only trivial things too

--
-- Note that the last chunk we delete might have fewer elements than @n@.
removes :: forall a. Int -> [a] -> [[a]]
removes k = \xs -> go xs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to do this? Happy with the rest

--
-- Note that the last chunk we delete might have fewer elements than @n@.
removes :: forall a. Int -> [a] -> [[a]]
removes k = \xs -> go xs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
removes k = \xs -> go xs
removes k = go

dropSome ts = do
n <- takeWhile (> 0) $ iterate (`div` 2) (length ts)
ts' <- removes n ts
pure . TreeT . pure $ interleave ts'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's quite a lot going on here. It's not terribly obvious to me what the overall goal is and how each piece fits into it. Why chunk this way? Why choose these chunk sizes? How does the recursive call to interleave contribute to the whole job? Is there a way to track length information to avoid calling length repeatedly?

@@ -320,7 +339,7 @@ interleave :: forall m a. Monad m => [NodeT m a] -> NodeT m [a]
interleave ts =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interleave is begging for documentation. Detailed documentation would be great; barring that, a clear statement of its general goals would be helpful.

@jacobstanley jacobstanley merged commit d793fb9 into hedgehogqa:master Jan 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants