grisette-0.7.0.0: src/Grisette/Internal/TH/DeriveWithHandlers.hs
{-# LANGUAGE TupleSections #-}
-- |
-- Module : Grisette.Internal.TH.DeriveWithHandlers
-- 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.DeriveWithHandlers
( deriveWithHandlers,
)
where
import Control.Monad (foldM, unless, when)
import Data.List (transpose)
import qualified Data.Map as M
import Data.Maybe (fromMaybe, mapMaybe)
import Grisette.Internal.TH.DeriveInstanceProvider
( DeriveInstanceProvider (instanceDeclaration),
)
import Grisette.Internal.TH.DeriveTypeParamHandler
( DeriveTypeParamHandler (handleBody, handleTypeParams),
SomeDeriveTypeParamHandler,
)
import Grisette.Internal.TH.Util
( allSameKind,
dropNTypeParam,
reifyDatatypeWithFreshNames,
substDataType,
)
import Language.Haskell.TH (Dec, Name, Q)
import Language.Haskell.TH.Datatype
( ConstructorInfo (constructorFields),
DatatypeInfo (datatypeCons, datatypeVars),
datatypeType,
reifyDatatype,
tvName,
)
transposeMatrix :: Int -> [[a]] -> [[a]]
transposeMatrix n [] = replicate n []
transposeMatrix _ x = transpose x
-- | Derive instances for a list of types with a list of handlers and a
-- provider.
deriveWithHandlers ::
(DeriveInstanceProvider provider) =>
[SomeDeriveTypeParamHandler] ->
provider ->
Bool ->
Int ->
[Name] ->
Q [Dec]
deriveWithHandlers
handlers
provider
ignoreBodyConstraints
numDroppedTailTypes
names = do
when (numDroppedTailTypes < 0) $
fail "deriveWithHandlers: numDroppedTailTypes must be non-negative"
when (numDroppedTailTypes > 0 && not ignoreBodyConstraints) $
fail $
"deriveWithHandlers: ignoreBodyConstraints must be True if "
<> "numDroppedTailTypes > 0"
when (null names) $
fail "deriveWithHandlers: no types provided"
datatypes <-
if length names == 1
then mapM reifyDatatype names
else mapM reifyDatatypeWithFreshNames names
let tyVars =
transposeMatrix 0 $
map
(reverse . drop numDroppedTailTypes . reverse . datatypeVars)
datatypes
unless (all allSameKind tyVars) $
fail "deriveWithHandlers: all type variables must be aligned"
tyVarsWithConstraints <-
foldM
(flip $ handleTypeParams (length datatypes))
( map
(\tyVarList -> ((,Nothing) <$> tyVarList, Nothing))
tyVars
)
handlers
let allTyVarsConstraints =
concatMap (fromMaybe [] . snd) tyVarsWithConstraints
let tvWithSubst =
transposeMatrix (length datatypes) $
fst <$> tyVarsWithConstraints
let substMaps =
map
( M.fromList
. mapMaybe
( \(tv, t) -> do
substTy <- t
return (tvName tv, substTy)
)
)
tvWithSubst
let substedTypes = zipWith substDataType datatypes substMaps
tys <-
mapM (dropNTypeParam numDroppedTailTypes . datatypeType) substedTypes
allConstraints <-
( if ignoreBodyConstraints
then return allTyVarsConstraints
else do
bodyConstraints <- handleBodyWithHandlers substedTypes handlers
return $ allTyVarsConstraints ++ bodyConstraints
)
instanceDeclaration
provider
(fst <$> tyVarsWithConstraints)
allConstraints
tys
where
handleBodyWithHandlers datatypes handlers = do
let cons = datatypeCons <$> datatypes
let zippedFields = zipFields cons
concat <$> traverse (`handleBody` zippedFields) handlers
zipFields cons = transpose $ concatMap constructorFields <$> cons