packages feed

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 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