Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hashing by Persistent's Entity key?

Tags:

haskell

yesod

I'm trying to build a Data.HashMap structure by grouping multiple Persistent entities by their foreign key field. E.g. say I have these two entities (straight out of the Yesod book)

Person
    name String
    age Int Maybe
    deriving Show
BlogPost
    title String
    authorId PersonId
    deriving Show

and I want a:

HashMap PersonId [BlogPost]

The challenge here is that PersonId does not directly implement Hashable (from Data.Hashable) and additionally it's abstracted depending on which DB you're using (Postgres in my case).

I suppose (just a wild beginner guess) I could implement Hashable for PersonId myself by doing fromPersistValue and a read into an Int64, for which there's a Hashable implementation already, but I imagine there might be a less convoluted way of achieving that.

Any suggestions?

like image 551
Alexandr Kurilin Avatar asked Jun 01 '14 02:06

Alexandr Kurilin


2 Answers

You can do this for the price of a few orphan instances. You can find the complete code below.

PersonId is just an alias for KeyBackend SqlBackend Person, so the instance Hashable (KeyBackend b e) is what you need. The rest of the declarations derive Hashable for other types that may end up being parts of PersonId.

{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveGeneric #-}

module HashableInstances where

import Database.Persist
import GHC.Generics
import Data.Hashable
import Data.Time
import Data.Fixed

instance HasResolution e => Hashable (Fixed e) where
  hashWithSalt s = (hashWithSalt s :: Double -> Int) . realToFrac
deriving instance Generic Day
instance Hashable Day
deriving instance Generic LocalTime
instance Hashable LocalTime
deriving instance Generic TimeOfDay
instance Hashable TimeOfDay
deriving instance Generic TimeZone
instance Hashable TimeZone
instance Hashable DiffTime where
  hashWithSalt s = (hashWithSalt s :: Double -> Int) . realToFrac
deriving instance Generic UTCTime
instance Hashable UTCTime
deriving instance Generic ZonedTime
instance Hashable ZonedTime
deriving instance Generic ZT
instance Hashable ZT
deriving instance Generic PersistValue
instance Hashable PersistValue
deriving instance Generic (KeyBackend b e)
instance Hashable (KeyBackend b e)
like image 166
Roman Cheplyaka Avatar answered Nov 13 '22 01:11

Roman Cheplyaka


Does it have to be a HashMap? Data.Map only needs Ord. The Hashable class is a bit dodgy and could use a fix-up anyway.

Unless you know what you're doing and understand the flaws in the hashing behavior of Hashable you should never use HashMap where a Data.Map would do.

This has the side effect of avoiding unnecessary orphan instances. (More things are Ord than Hashable)

like image 44
bitemyapp Avatar answered Nov 13 '22 02:11

bitemyapp