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

Memoization #25

Open
edsko opened this issue Mar 11, 2023 · 3 comments
Open

Memoization #25

edsko opened this issue Mar 11, 2023 · 3 comments
Labels
enhancement New feature or request priority: low

Comments

@edsko
Copy link
Collaborator

edsko commented Mar 11, 2023

We have the withoutShrinking combinator that makes generators that we want to run only once against a SampleTree. Unfortunately, we currently have to re-run those generators every single time they run, which leads to pretty bad performance; this is most notable in tests that depend on fromShrinkTree; see the TestSuite.Sanity.Functions.StringToBool test one a particularly bad example.

It feels like it should be relatively simple to memoize these results. For example, we could imagine that shrinking could return new generators, such that if we have

data Gen a where
  ..
  Once :: (SampleTree -> (a, SampleTree)) -> Gen a

then

shrink (Once f) st = first Pure $ f st

replacing the generator by a constant one that just returns the value it produces. Unfortunately, this does not work: we cannot return new generators, because generators are monadic (how would we update a continuation a -> Gen b?).

The only state we have is in the SampleTree. Now, we could of course do something like this:

data Sample = .. | NotShrunk Word64 (Maybe Dynamic)

where we cache the constructed value right in the same tree. This too would make sense, but it's unclear how this works with context re-interpretation . For example, what if we have a generator such as

example :: Gen ..
example = do
    b <- bool
    if b then once ..
         else once ..

We now run two different generators against the same sample tree; surely we don't expect to get the result from one of those generators in the other one? That would be very confusing (consider the case where one generator is prim, and the other (* 2) <$> prim or something like that; it would be very strange if we got an odd number out of that generator).

@edsko
Copy link
Collaborator Author

edsko commented Mar 11, 2023

Perhaps this is not sufficient, actually. When we are memoizing a shrink tree, we would still be retracing our steps through that shrink tree on each iteration; we would merely avoid building the shrink tree, which might not actually save us all that much time. Ideally we would memoize the most recently generated value, so that we only need to take a single step.

@edsko
Copy link
Collaborator Author

edsko commented Mar 13, 2023

This is less of a priority now that we can generate functions in an idiomatic way, not relying on shrink trees (#29).

@edsko edsko added enhancement New feature or request priority: low labels Mar 13, 2023
@edsko
Copy link
Collaborator Author

edsko commented Mar 13, 2023

Perhaps https://hackage.haskell.org/package/chimera can help here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request priority: low
Projects
None yet
Development

No branches or pull requests

1 participant