-
Notifications
You must be signed in to change notification settings - Fork 187
Implement fromSetA for IntMap and Map #1163
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
Conversation
treeowl
left a comment
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.
Whoops ... I forgot that you'll need CPP around the coercions.
treeowl
left a comment
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 forgot to mention the need for QuickCheck properties. At the least, we need one that will verify that the effect order is correct. The simplest thing will probably be to work in Writer, recording each key as it's reached.
|
Ideally, we'd want strictness tests too. I'm just waking up, so I haven't re-reviewed yet. |
|
No worries, not requiring a review just yet since I've not done benchmarks. I appreciate the comments, there's oh so much to make sure is done. |
|
Good call on the tests, the ordering of map's fromSetA was wrong (it was doing root, then left, then right, should've been left, root, right) |
| prop_strictFromSetA fun set = | ||
| isBottom (getSolo (M.fromSetA f set)) === any (isBottom . getSolo . f) (IntSet.toList set) | ||
| where | ||
| f = MkSolo . coerce (applyFunc fun) :: Key -> Solo 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.
What's this coercing from/to?
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.
looks like this is a confusing pattern in this and other testing files, where we coerce to and from Bot A instead of just using it. I'll remove it in this case, but there are 142 usages of coerce in just intmap-strictness alone so I won't change all of them for now.
|
This all looks great, pending benchmarks. As long as |
|
Turns out pre-9.0.2 Solo does not exist, so I'll whip up an equivalent. |
Ugh. And then there was a nontrivial amount of churn after its introduction with the (unfortunate, IMO) constructor name change. Feel free to roll your own unconditionally if you don't want to futz with all that CPP. |
|
Wait a sec ... That wasn't very smart of me.... In the test suite we can use whatever packages we like. So we can just depend on |
|
and of course liftA2 was not a method of Applicative at some point, so I will switch over to OneTuple as suggested. No worries on wasting my time, sorry to waste yours. |
395a79a to
a3b7f1c
Compare
|
This time I tried to compile with ghc-9.2 so I know that this works with at least 9.2.2. |
|
I wish there were some way to tell GitHub to approve CI for a new contributor before their first PR is merged. Grrr. |
|
CPS writer is overkill here. We're just using |
|
as you say, CPS writer is overkill. Unless you really want it, I'll switch to lazy writer and call it a day. |
|
That's fine. Our trees are balanced, so it shouldn't be a huge perf hit. |
|
I've added INLINABLE and as expected it really improves the benchmarks, nice to see things working |
7262750 to
881ec82
Compare
|
We don't currently have any benchmarks for traversal functions, so adding benchmarks for fromSetA and traversal functions that use transformers and such would be great in a followup PR. |
|
I would be more comfortable if I saw a few hundred milliseconds rather than microseconds. Is that feasible? Also, you're only using an |
|
I can easily just bump the bound size up arbitrarily. I'm worried about bumping the benchmarks above what is reasonable for people to run normally. I'll change the sets used to be the random ones, as well. |
|
How long is it taking you to run the benchmarks? |
|
When I was running all of them it was taking a while, it takes less time when I'm just running these. Increaasing the test cases for |
|
Then don't bump them up too much in the PR, but do bump them up temporarily to produce some bugger numbers. We can deal with the larger question as a separate issue. Note that you can run specific benchmarks, or even just comment out everything else, to get the numbers you need. |
treeowl
left a comment
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.
Nice results. We still need to work on some benchmarking issues, but this PR is not the place. Please remember also to update the change log.
8fad662 to
8ba0cff
Compare
treeowl
left a comment
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.
Thanks again for all your work!
|
I'll merge once the final CI run completes, unless @meooow25 has any objections. |
meooow25
left a comment
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.
Looks like I'm late, but leaving some comments anyway.
| instance CoArbitrary a => CoArbitrary (Bot a) where | ||
| coarbitrary (Bot x) = coarbitrary x | ||
|
|
||
| instance Function a => Function (Bot a) where | ||
| function = functionMap (\(Bot x) -> x) Bot | ||
|
|
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.
These shouldn't be necessary.
In fact if we need them, we're doing something odd. There is no reason to generate functions that take in Bot 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.
I don't know why you say that. It avoids having to unwrap the Bots manually.
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'm happy to remove this theoretically. Note that Fun from quickcheck cannot generate lazy functions, so it is a little bad to have this instance theoretically.
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 avoids having to unwrap the
Bots manually.
Do you have any example of where we would want this instance?
| prop_strictFromSetA :: Func Key (Bot A) -> IntSet -> Property | ||
| prop_strictFromSetA fun set = | ||
| isBottom (getSolo (M.fromSetA (MkSolo . f) set)) === any (isBottom . f) (IntSet.toList set) | ||
| where | ||
| f = applyFunc fun |
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.
This is a weird way to do this.
What we want in the test here is a Key -> Solo A where A can be bottom. That translates to generating an arbitrary Func Key (Solo (Bot A)), which can be coerced to Key -> Solo 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.
Func Key (Bot A) and Func Key (Solo (Bot A)) are equivalent, and the only difference is whether you unwrap the Solo in the RHS or you wrap with Solo on the LHS. I preferred the former because it's clearer what the functions is meant to do - produce a bottom value from a key.
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 not about what might seem clearer in this particular case, it's a simple mechanical translation from the type of the function being tested which can be applied to all tests here.
|
@meooow25 I'm sorry I jumped the gun on the merge. |
L0neGamer
left a comment
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'll look into improvements in a follow up PR, and request your approval for that in time @ meooow25
| prop_strictFromSetA :: Func Key (Bot A) -> IntSet -> Property | ||
| prop_strictFromSetA fun set = | ||
| isBottom (getSolo (M.fromSetA (MkSolo . f) set)) === any (isBottom . f) (IntSet.toList set) | ||
| where | ||
| f = applyFunc fun |
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.
Func Key (Bot A) and Func Key (Solo (Bot A)) are equivalent, and the only difference is whether you unwrap the Solo in the RHS or you wrap with Solo on the LHS. I preferred the former because it's clearer what the functions is meant to do - produce a bottom value from a key.
| instance CoArbitrary a => CoArbitrary (Bot a) where | ||
| coarbitrary (Bot x) = coarbitrary x | ||
|
|
||
| instance Function a => Function (Bot a) where | ||
| function = functionMap (\(Bot x) -> x) Bot | ||
|
|
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'm happy to remove this theoretically. Note that Fun from quickcheck cannot generate lazy functions, so it is a little bad to have this instance theoretically.
meooow25
left a comment
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.
@meooow25 I'm sorry I jumped the gun on the merge.
No worries, these changes are fine. And of course, we can adjust things later if we need to.
I'll look into improvements in a follow up PR, and request your approval for that in time @ meooow25
Thank you, that would be great!
| instance CoArbitrary a => CoArbitrary (Bot a) where | ||
| coarbitrary (Bot x) = coarbitrary x | ||
|
|
||
| instance Function a => Function (Bot a) where | ||
| function = functionMap (\(Bot x) -> x) Bot | ||
|
|
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 avoids having to unwrap the
Bots manually.
Do you have any example of where we would want this instance?
| prop_strictFromSetA :: Func Key (Bot A) -> IntSet -> Property | ||
| prop_strictFromSetA fun set = | ||
| isBottom (getSolo (M.fromSetA (MkSolo . f) set)) === any (isBottom . f) (IntSet.toList set) | ||
| where | ||
| f = applyFunc fun |
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 not about what might seem clearer in this particular case, it's a simple mechanical translation from the type of the function being tested which can be applied to all tests here.



Initial stab at #875
Happy to adjust, test, or benchmark as wanted. I'll look into benchmark comparisons for the changed functions later.