hashable-generics (empty) → 1.1
raw patch · 5 files changed
+273/−0 lines, 5 filesdep +QuickCheckdep +basedep +hashablesetup-changed
Dependencies added: QuickCheck, base, hashable, hashable-generics, test-framework, test-framework-quickcheck2
Files
- Data/Hashable/Generic.hs +86/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- hashable-generics.cabal +41/−0
- test/Suite.hs +114/−0
+ Data/Hashable/Generic.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE TypeOperators, FlexibleContexts, BangPatterns #-}+-- | Stability: stable+-- Portability: GHC+--+-- NOTE: This module exports the Hashable class for convenience. If this+-- becomes a problem, just use a qualified import.+module Data.Hashable.Generic ( gHashWithSalt+ , Hashable(..)+ ) where++import Data.Hashable+import GHC.Generics++-- | "GHC.Generics"-based 'hashWithSalt' implementation+--+-- This provides a generic `hashWithSalt` implementation for one type at a+-- time. If the type of the value 'gHashWithSalt' is asked to hash+-- contains values of other types, those types have to provide+-- 'Hashable' instances. This also means that recursive types can only+-- be used with 'gHashWithSalt' if a 'Hashable' instance has been defined+-- as well (see examples below).+--+-- The typical usage for 'gHashWithSalt' is for reducing boilerplate code+-- when defining 'Hashable' instances for ordinary algebraic+-- datatypes. See the code below for some simple usage examples:+--+-- > {-# LANGUAGE DeriveGeneric #-}+-- >+-- > import Data.Hashable+-- > import Data.Hashable.Generic ( gHashWithSalt )+-- > import GHC.Generics+-- >+-- > -- simple record+-- > data Foo = Foo AccountId Name Address+-- > deriving Generic+-- >+-- > type Address = [String]+-- > type Name = String+-- > newtype AccountId = AccountId Int+-- >+-- > instance Hashable AccountId+-- > instance Hashable Foo where hashWithSalt = gHashWithSalt+-- >+-- > -- recursive list-like type+-- > data N = Z | S N deriving Generic+-- >+-- > instance Hashable N where hashWithSalt = gHashWithSalt+-- >+-- > -- parametric and recursive type+-- > data Bar a = Bar0 | Bar1 a | Bar2 (Bar a)+-- > deriving Generic+-- >+-- > instance Hashable a => Hashable (Bar a) where hashWithSalt = gHashWithSalt+--+-- Note: The 'GHashable' type-class showing up in the type-signature is+-- used internally and not exported on purpose currently+gHashWithSalt :: (Generic a, GHashable (Rep a)) => Int -> a -> Int+gHashWithSalt salt x = gHashWithSalt_ salt $ from x+{-# INLINE gHashWithSalt #-}++-- | Hidden internal type class.+class GHashable f where+ gHashWithSalt_ :: Int -> f a -> Int++instance GHashable U1 where+ gHashWithSalt_ !salt U1 = hashWithSalt salt ()+ {-# INLINE gHashWithSalt_ #-}++instance Hashable a => GHashable (K1 i a) where+ gHashWithSalt_ !salt = hashWithSalt salt . unK1+ {-# INLINE gHashWithSalt_ #-}++instance GHashable a => GHashable (M1 i c a) where+ gHashWithSalt_ !salt = gHashWithSalt_ salt . unM1+ {-# INLINE gHashWithSalt_ #-}++instance (GHashable a, GHashable b) => GHashable (a :*: b) where+ gHashWithSalt_ !salt (x :*: y) = gHashWithSalt_ (gHashWithSalt_ salt x) y+ {-# INLINE gHashWithSalt_ #-}++-- This instance is suboptimal (with the salt+1 hackery). Is there a better way+-- to be doing this so that both choices can be unique?+instance (GHashable a, GHashable b) => GHashable (a :+: b) where+ gHashWithSalt_ !salt (L1 x) = gHashWithSalt_ salt x+ gHashWithSalt_ !salt (R1 x) = gHashWithSalt_ (salt+1) x+ {-# INLINE gHashWithSalt_ #-}
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Clark Gaebel++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Clark Gaebel nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hashable-generics.cabal view
@@ -0,0 +1,41 @@+name: hashable-generics+version: 1.1+synopsis: Automatically generates Hashable instances with GHC.Generics.+description:+ This package provides a "GHC.Generics"-based 'Data.Hashable.Generic.gHashWithSalt'+ function which can be used for providing a 'gHashWithSalt' implementation.+ See the documentation for the 'gHashWithSalt' function in the+ "Data.Hashable.Generic" module to get start.+ .+ This package is heavily inspired by deepseq-generics, which you may also find+ useful.+homepage: https//github.com/wowus/hashable-generics+license: BSD3+license-file: LICENSE+author: Clark Gaebel+copyright: 2012, Clark Gaebel+maintainer: cgaebel@uwaterloo.ca+category: Data+build-type: Simple+cabal-version: >=1.8+tested-with: GHC >=7.6++source-repository head+ type: git+ location: https://github.com/wowus/hashable-generics.git++library+ exposed-modules: Data.Hashable.Generic+ build-depends: base >=4.5 && <4.7+ , hashable ==1.1.*++test-suite tests+ type: exitcode-stdio-1.0+ main-is: Suite.hs+ hs-source-dirs: test+ build-depends: base+ , hashable+ , test-framework+ , test-framework-quickcheck2+ , QuickCheck+ , hashable-generics
+ test/Suite.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, BangPatterns #-}+{-# OPTIONS_GHC -Wall #-}+module Main where++import Control.Applicative+import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck++import Data.Hashable.Generic++import GHC.Generics++main :: IO ()+main = defaultMain tests++tests :: [Test]+tests = [ testGroup "Documentation"+ [ testProperty "Simple Record" simpleRecord+ , testProperty "Recursive Type" recursiveType+ , testProperty "Parametric and Recursive Type" paraRecursive+ ]+ ]++data FooA = FooA AccountId Name Address+ deriving (Generic, Show)++instance Arbitrary FooA where+ arbitrary = FooA <$> arbitrary+ <*> arbitrary+ <*> arbitrary++data FooB = FooB AccountId Name Address++type Address = [String]+type Name = String++newtype AccountId = AccountId Int+ deriving (Hashable, Show)++instance Arbitrary AccountId where+ arbitrary = AccountId <$> arbitrary++instance Hashable FooA where+ hashWithSalt = gHashWithSalt++instance Hashable FooB where+ hashWithSalt salt (FooB ac n ad) = hashWithSalt+ (hashWithSalt+ (hashWithSalt salt ac)+ n)+ ad++aToB :: FooA -> FooB+aToB (FooA ac n ad) = FooB ac n ad++simpleRecord :: FooA -> Bool+simpleRecord a = let b = aToB a+ in hash a == hash b++data NA = ZA | SA NA+ deriving (Generic, Show)++data NB = ZB | SB NB++instance Hashable NA where+ hashWithSalt = gHashWithSalt++instance Hashable NB where+ hashWithSalt !salt ZB = hashWithSalt salt ()+ hashWithSalt !salt (SB xs) = hashWithSalt (salt+1) xs++instance Arbitrary NA where+ arbitrary = lst2A <$> arbitrary++lst2A :: [()] -> NA+lst2A [] = ZA+lst2A (_:xs) = SA $ lst2A xs++na2nb :: NA -> NB+na2nb ZA = ZB+na2nb (SA x) = SB $ na2nb x++recursiveType :: NA -> Bool+recursiveType a = let b = na2nb a+ in hash a == hash b++data BarA a = BarA0 | BarA1 a | BarA2 (BarA a)+ deriving (Generic, Show)++data BarB a = BarB0 | BarB1 a | BarB2 (BarB a)++instance Arbitrary a => Arbitrary (BarA a) where+ arbitrary = oneof [ return BarA0+ , BarA1 <$> arbitrary+ , BarA2 <$> arbitrary+ ]++instance Hashable a => Hashable (BarA a) where+ hashWithSalt = gHashWithSalt++instance Hashable a => Hashable (BarB a) where+ hashWithSalt !salt BarB0 = hashWithSalt salt ()+ hashWithSalt !salt (BarB1 x) = hashWithSalt (salt+1) x+ hashWithSalt !salt (BarB2 x) = hashWithSalt (salt+2) x++barA2B :: BarA a -> BarB a+barA2B BarA0 = BarB0+barA2B (BarA1 x) = BarB1 x+barA2B (BarA2 x) = BarB2 $ barA2B x++paraRecursive :: BarA Int -> Bool+paraRecursive a = let b = barA2B a+ in hash a == hash b