-
Notifications
You must be signed in to change notification settings - Fork 86
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
Higher kinded classes #124
Higher kinded classes #124
Conversation
Generics support for |
@RyanGlScott I knew you would show up ;) Now that I think about it, that would be useful (especially if I'm working with |
Sure thing! I recommend using a trick like |
@RyanGlScott Thanks. That's a really good trick. I was just about to duplicate everything. |
Generics for I'm guessing that its leftover from a different approach to hashing sums that was removed. |
@andrewthad I think you're missing a |
hashSum toHash !salt !code _ (M1 x) = ghashWithSalt toHash (hashWithSalt salt code) x | ||
{-# INLINE hashSum #-} | ||
|
||
instance GSum One Par1 where |
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.
Is this case even necessary? It looks like GSum
bottoms out at C1 c a
(at which point it relies on a
being a GHashable
), so I don't believe this case is ever reachable, no?
data Zero = Zero | ||
data One = One | ||
|
||
data ToHash arity a where |
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.
Minor bikeshedding: I'd call this datatype HashArgs
instead (I used the name ToArgs
in the aeson
example simply to distinguish it from FromArgs
, as the former was for ToJSON
and the latter was for FromJSON
).
@RyanGlScott Thanks for the feedback. I've address both of your comments. |
|
||
-- This function is copied from Data.Functor.Classes, which does | ||
-- not export it. | ||
showsUnaryWith :: (Int -> a -> ShowS) -> String -> Int -> a -> ShowS |
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 function is copied from Data.Functor.Classes, which does not export it.
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. Thanks for pointing that out.
Not sure what's causing the build failure. The failing line is:
: cannot satisfy -package-id hashable-1.2.4.0-inplace: Other than that, I still need to add tuple instances. Any other thoughts or concerns? |
I've seen that "shadowed by package" error before. It's because some dependencies in the test suite depend on Fixing these sorts of issues is never fun. For one thing, you absolutely cannot list Since you can't depend on the Now here's the kicker. Even after you do all this, tl;dr We're kind of in a rut as far as configuring the tests go. I wouldn't recommend trying to fix the issue in this PR, as getting it right often involves several rounds of back-and-forth between Travis to get the dependency installation just right. |
Thanks for clearing that up. I've wondered about test suites that use other-modules like that before. Definitely won't try to fix that in this PR. I'll go ahead and get the tuple instances ready. |
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 nice overall. Just some nits/questions.
-- 'liftHashWithSalt' to ignore its first argument when a | ||
-- value is actually available for it to work on. | ||
instance Hashable1 Hashed where | ||
liftHashWithSalt _ s (Hashed _ h) = defaultHashWithSalt s h |
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 a bit unsettling indeed. I wonder if Hashed
to cache the salt it used last time and make a comparison to it and rehash if it's not the same.
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 Hashed
can only be created by hashed
, that would require introducing another function hashedWithSalt
and changing Hashed
to:
data Hashed = Hashed a (Maybe Int)
That being said, I don't like that because it's easily to accidentally use incorrectly, circumventing the caching its supposed to provide. My preference would be to leave it as is and document the counterintuitive behavior of hashWithSalt
on a Hashed
. For the libraries I'm working on, caching is the most important thing that Hashed
provides.
-- This instance is a little unsettling. It is unusal for | ||
-- 'liftHashWithSalt' to ignore its first argument when a | ||
-- value is actually available for it to work on. | ||
instance Hashable1 Hashed where |
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.
Shouldn't this instance be in Lifted
?
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.
That would orphan the instance. I was trying to avoid doing that, but I could move Hashed into another module.
-- instances for @Data.Functor.Classes@ higher rank typeclasses | ||
-- in base-4.9 and onward. | ||
#if MIN_VERSION_base(4,9,0) | ||
instance Eq1 Hashed where |
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.
Similarly for these instances. Shouldn't they be in Lifted
? We might need to move Hashed
to an internal module to avoid the instances being orphaned.
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.
Do you think that putting it into Data.Hashable.Class would be alright? Or would you prefer a new internal module?
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.
Class
is fine.
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 has now been moved.
|
||
-- | The class of types that can be generically hashed. | ||
class GHashable f where | ||
ghashWithSalt :: Int -> f a -> Int | ||
class GHashable arity f where |
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.
We need to make sure that this doesn't introduce any badly generated Core for derived instances. Can you check e.g. that deriving for a simple data type yields as good Core as it did before?
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.
Yeah, I don't really know how to look at Core, but I'm at Hac Phi, so I'll get someone to help me with it 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.
Oddly, the changes to GHashable have improved performance. I'll detail this in a top level comment.
Ben Gamari was able to help look into the differences in the generated core, and the new generic implementation of
|
Also, just in case anyone wants to look at master but with the backported benchmarks, here it is: https://github.com/andrewthad/hashable/tree/master_with_examples. I think that I still need to correct some of the examples I gave in the haddocks. @tibbe What were your thoughts on my response concerning Hashed? |
Just wanted to check in and see if there is anything I can to do keep this PR going forward. |
This implements what is proposed in #114. It is not yet complete. I still need to add more instances. There are two deviations from the original proposal:
liftHash
is not included. The reason for this are found in the documentation inData.Hashable.Lifted
.Hashable2
class was included as well. This is mostly for consistency with other packages that provide higher rank typeclass variants.In some of existing
Hashable
instances, I callhashWithSalt1
to avoid duplicating code. You can look at the instance for[]
to see this. I do not believe that this will hurt performance because the inliner should expand these.The example instances I provide in
Data.Hashable.Lifted
have not been typechecked and are likely broken. I'll get to that soon.Let me know at this point if there are any concerns so that the can be addressed before I write all the remaining instances.