packages feed

grisette-0.12.0.0: src/Grisette/Internal/TH/Derivation/DeriveHashable.hs

{-# LANGUAGE TemplateHaskell #-}

-- |
-- Module      :   Grisette.Internal.TH.Derivation.DeriveHashable
-- Copyright   :   (c) Sirui Lu 2024
-- License     :   BSD-3-Clause (see the LICENSE file)
--
-- Maintainer  :   siruilu@cs.washington.edu
-- Stability   :   Experimental
-- Portability :   GHC only
module Grisette.Internal.TH.Derivation.DeriveHashable
  ( deriveHashable,
    deriveHashable1,
    deriveHashable2,
  )
where

import Data.Hashable (Hashable (hashWithSalt))
import Data.Hashable.Lifted
  ( Hashable1 (liftHashWithSalt),
    Hashable2 (liftHashWithSalt2),
  )
import Grisette.Internal.TH.Derivation.Common (DeriveConfig)
import Grisette.Internal.TH.Derivation.UnaryOpCommon
  ( UnaryOpClassConfig
      ( UnaryOpClassConfig,
        unaryOpAllowExistential,
        unaryOpConfigs,
        unaryOpContextNames,
        unaryOpExtraVars,
        unaryOpInstanceNames,
        unaryOpInstanceTypeFromConfig
      ),
    UnaryOpConfig (UnaryOpConfig),
    UnaryOpFieldConfig
      ( UnaryOpFieldConfig,
        extraLiftedPatNames,
        extraPatNames,
        fieldCombineFun,
        fieldFunExp,
        fieldResFun
      ),
    defaultFieldFunExp,
    defaultUnaryOpInstanceTypeFromConfig,
    genUnaryOpClass,
  )
import Language.Haskell.TH (Dec, Name, Q)

hashableConfig :: UnaryOpClassConfig
hashableConfig =
  UnaryOpClassConfig
    { unaryOpConfigs =
        [ UnaryOpConfig
            UnaryOpFieldConfig
              { extraPatNames = ["salt"],
                extraLiftedPatNames = const [],
                fieldCombineFun =
                  \_ _ _ _ [salt] exp -> do
                    r <-
                      foldl
                        (\salt exp -> [|$(return exp) $salt|])
                        (return salt)
                        exp
                    return (r, [True]),
                fieldResFun = \_ _ _ _ fieldPat fieldFun -> do
                  r <- [|\salt -> $(return fieldFun) salt $(return fieldPat)|]
                  return (r, [False]),
                fieldFunExp =
                  defaultFieldFunExp
                    ['hashWithSalt, 'liftHashWithSalt, 'liftHashWithSalt2]
              }
            ['hashWithSalt, 'liftHashWithSalt, 'liftHashWithSalt2]
        ],
      unaryOpInstanceNames =
        [''Hashable, ''Hashable1, ''Hashable2],
      unaryOpExtraVars = const $ return [],
      unaryOpInstanceTypeFromConfig = defaultUnaryOpInstanceTypeFromConfig,
      unaryOpAllowExistential = True,
      unaryOpContextNames = Nothing
    }

-- | Derive 'Hashable' instance for a data type.
deriveHashable :: DeriveConfig -> Name -> Q [Dec]
deriveHashable deriveConfig = genUnaryOpClass deriveConfig hashableConfig 0

-- | Derive 'Hashable1' instance for a data type.
deriveHashable1 :: DeriveConfig -> Name -> Q [Dec]
deriveHashable1 deriveConfig = genUnaryOpClass deriveConfig hashableConfig 1

-- | Derive 'Hashable2' instance for a data type.
deriveHashable2 :: DeriveConfig -> Name -> Q [Dec]
deriveHashable2 deriveConfig = genUnaryOpClass deriveConfig hashableConfig 2