grisette-0.11.0.0: src/Grisette/Internal/Unified/UnifiedFun.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Grisette.Internal.Unified.UnifiedFun
-- 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.Unified.UnifiedFun
( UnifiedFunConstraint,
UnifiedFun (..),
unifiedFunInstanceName,
genUnifiedFunInstance,
GetFun2,
GetFun3,
GetFun4,
GetFun5,
GetFun6,
GetFun7,
GetFun8,
)
where
#if MIN_VERSION_base(4,20,0)
#else
import Data.Foldable (Foldable (foldl'))
#endif
import Control.DeepSeq (NFData)
import Data.Binary (Binary)
import Data.Bytes.Serial (Serial)
import Data.Hashable (Hashable)
import qualified Data.Kind
import Data.Serialize (Serialize)
import Data.Typeable (Typeable)
import GHC.TypeLits (KnownNat, Nat, type (<=))
import Grisette.Internal.Core.Data.Class.EvalSym (EvalSym)
import Grisette.Internal.Core.Data.Class.ExtractSym (ExtractSym)
import Grisette.Internal.Core.Data.Class.Function (Apply (FunType), Function)
import Grisette.Internal.Core.Data.Class.Mergeable (Mergeable)
import Grisette.Internal.Core.Data.Class.PPrint (PPrint)
import Grisette.Internal.Core.Data.Class.SubstSym (SubstSym)
import Grisette.Internal.Core.Data.Class.ToCon (ToCon)
import Grisette.Internal.Core.Data.Class.ToSym (ToSym)
import Grisette.Internal.SymPrim.AlgReal (AlgReal)
import Grisette.Internal.SymPrim.BV (IntN, WordN)
import Grisette.Internal.SymPrim.FP (FP, ValidFP)
import Grisette.Internal.SymPrim.SymAlgReal (SymAlgReal)
import Grisette.Internal.SymPrim.SymBV (SymIntN, SymWordN)
import Grisette.Internal.SymPrim.SymBool (SymBool)
import Grisette.Internal.SymPrim.SymFP (SymFP)
import Grisette.Internal.SymPrim.SymInteger (SymInteger)
import Grisette.Internal.SymPrim.SymTabularFun (type (=~>))
import Grisette.Internal.SymPrim.TabularFun (type (=->))
import Grisette.Internal.Unified.EvalModeTag (EvalModeTag (C, S))
import Grisette.Internal.Unified.Theories
( TheoryToUnify (UAlgReal, UBool, UFP, UFun, UIntN, UInteger, UWordN),
)
import Grisette.Internal.Unified.UnifiedAlgReal (GetAlgReal)
import Grisette.Internal.Unified.UnifiedBV (UnifiedBVImpl (GetIntN, GetWordN))
import Grisette.Internal.Unified.UnifiedBool (UnifiedBool (GetBool))
import Grisette.Internal.Unified.UnifiedFP (UnifiedFPImpl (GetFP))
import Grisette.Internal.Unified.UnifiedInteger (GetInteger)
import Language.Haskell.TH
( DecsQ,
Pred,
Q,
TyLit (NumTyLit),
Type (AppT, ConT, ForallT, LitT, VarT),
appT,
classD,
conT,
instanceD,
mkName,
newName,
promotedT,
varT,
)
import qualified Language.Haskell.TH
import Language.Haskell.TH.Datatype.TyVarBndr
( kindedTV,
mapTVFlag,
specifiedSpec,
tvName,
)
import Language.Haskell.TH.Syntax (Lift)
#if MIN_VERSION_template_haskell(2,21,0)
type TyVarBndrVis = Language.Haskell.TH.TyVarBndrVis
#elif MIN_VERSION_template_haskell(2,17,0)
type TyVarBndrVis = Language.Haskell.TH.TyVarBndr ()
#else
type TyVarBndrVis = Language.Haskell.TH.TyVarBndr
#endif
-- | Provide unified function types.
class UnifiedFun (mode :: EvalModeTag) where
-- | Get a unified function type. Resolves to t'Grisette.SymPrim.=->' in 'C'
-- mode, and t'Grisette.SymPrim.=~>' in 'S' mode.
type
GetFun mode =
(fun :: Data.Kind.Type -> Data.Kind.Type -> Data.Kind.Type) | fun -> mode
instance UnifiedFun 'C where
type GetFun 'C = (=->)
instance UnifiedFun 'S where
type GetFun 'S = (=~>)
-- | The unified function type with 2 arguments.
type GetFun2 mode a b = GetFun mode a b
-- | The unified function type with 3 arguments.
type GetFun3 mode a b c = GetFun mode a (GetFun mode b c)
-- | The unified function type with 4 arguments.
type GetFun4 mode a b c d = GetFun mode a (GetFun mode b (GetFun mode c d))
-- | The unified function type with 5 arguments.
type GetFun5 mode a b c d e =
GetFun mode a (GetFun mode b (GetFun mode c (GetFun mode d e)))
-- | The unified function type with 6 arguments.
type GetFun6 mode a b c d e f =
GetFun
mode
a
(GetFun mode b (GetFun mode c (GetFun mode d (GetFun mode e f))))
-- | The unified function type with 7 arguments.
type GetFun7 mode a b c d e f g =
GetFun
mode
a
( GetFun
mode
b
(GetFun mode c (GetFun mode d (GetFun mode e (GetFun mode f g))))
)
-- | The unified function type with 8 arguments.
type GetFun8 mode a b c d e f g h =
GetFun
mode
a
( GetFun
mode
b
( GetFun
mode
c
(GetFun mode d (GetFun mode e (GetFun mode f (GetFun mode g h))))
)
)
-- | The constraint for a unified function.
type UnifiedFunConstraint mode a b ca cb sa sb =
( Show (GetFun mode a b),
Binary (GetFun mode a b),
Serial (GetFun mode a b),
Serialize (GetFun mode a b),
NFData (GetFun mode a b),
Eq (GetFun mode a b),
EvalSym (GetFun mode a b),
ExtractSym (GetFun mode a b),
Mergeable (GetFun mode a b),
PPrint (GetFun mode a b),
SubstSym (GetFun mode a b),
Hashable (GetFun mode a b),
Lift (GetFun mode a b),
Typeable (GetFun mode a b),
ToCon (GetFun mode a b) (ca =-> cb),
ToCon (sa =~> sb) (GetFun mode a b),
ToSym (GetFun mode a b) (sa =~> sb),
ToSym (ca =-> cb) (GetFun mode a b),
Function (GetFun mode a b) a b,
Apply (GetFun mode a b),
FunType (GetFun mode a b) ~ (a -> b)
)
genInnerUnifiedFunInstance ::
String ->
TyVarBndrVis ->
[Pred] ->
[TyVarBndrVis] ->
[(Type, Type, Type)] ->
DecsQ
genInnerUnifiedFunInstance nm mode preds bndrs tys = do
x <- classD (goPred tys) (mkName nm) (mode : bndrs) [] []
dc <-
instanceD
(return preds)
(applyTypeList (promotedT 'C : additionalTypes))
[]
ds <-
instanceD
(return preds)
(applyTypeList (promotedT 'S : additionalTypes))
[]
return [x, dc, ds]
where
additionalTypes = (varT . tvName) <$> bndrs
applyTypeList = foldl appT (conT (mkName nm))
goPred :: [(Type, Type, Type)] -> Q [Pred]
goPred [] = fail "Empty list of function types, at least 2."
goPred [_] = return []
goPred (x : xs) = do
p1 <- pred x xs
pr <- goPred xs
return $ p1 : pr
listTys :: [(Type, Type, Type)] -> Q (Type, Type, Type)
listTys [] = fail "Should not happen"
listTys [(u, c, s)] = return (u, c, s)
listTys ((u, c, s) : xs) = do
(u', c', s') <- listTys xs
return
( AppT (AppT (AppT (ConT ''GetFun) (VarT $ tvName mode)) u) u',
AppT (AppT (ConT ''(=->)) c) c',
AppT (AppT (ConT ''(=~>)) s) s'
)
pred (ua, ca, sa) l = do
(ub, cb, sb) <- listTys l
[t|
UnifiedFunConstraint
$(return (VarT $ tvName mode))
$(return ua)
$(return ub)
$(return ca)
$(return cb)
$(return sa)
$(return sb)
|]
genOuterUnifiedFunInstance ::
String -> String -> TyVarBndrVis -> [Pred] -> [TyVarBndrVis] -> DecsQ
genOuterUnifiedFunInstance nm innerName mode preds bndrs = do
let bndrs' = mapTVFlag (const specifiedSpec) <$> bndrs
x <-
classD
( return
[ ForallT bndrs' preds $
foldl' AppT (ConT $ mkName innerName) $
VarT . tvName <$> mode : bndrs
]
)
(mkName nm)
[mode]
[]
[]
dc <-
instanceD
(return [])
(appT (conT $ mkName nm) (promotedT 'C))
[]
ds <-
instanceD
(return [])
(appT (conT $ mkName nm) (promotedT 'S))
[]
return [x, dc, ds]
-- | Generate unified function instance names.
unifiedFunInstanceName :: String -> [TheoryToUnify] -> String
unifiedFunInstanceName prefix theories =
prefix ++ "Fun" ++ (concatMap show theories)
-- | Generate unified function instances.
genUnifiedFunInstance :: String -> [TheoryToUnify] -> DecsQ
genUnifiedFunInstance prefix theories = do
modeName <- newName "mode"
let modeType = VarT modeName
allArgs <- traverse (genArgs modeType) theories
let baseName = unifiedFunInstanceName prefix theories
rinner <-
genInnerUnifiedFunInstance
baseName
(kindedTV modeName (ConT ''EvalModeTag))
(concatMap (\(_, p, _, _, _) -> p) allArgs)
(concatMap (\(t, _, _, _, _) -> t) allArgs)
((\(_, _, u, c, s) -> (u, c, s)) <$> allArgs)
router <-
if all (\(bndr, _, _, _, _) -> null bndr) allArgs
then return []
else
genOuterUnifiedFunInstance
("All" ++ baseName)
baseName
(kindedTV modeName (ConT ''EvalModeTag))
(concatMap (\(_, p, _, _, _) -> p) allArgs)
(concatMap (\(t, _, _, _, _) -> t) allArgs)
return $ rinner ++ router
where
genArgs ::
Type -> TheoryToUnify -> Q ([TyVarBndrVis], [Pred], Type, Type, Type)
genArgs mode UBool =
return
( [],
[],
AppT (ConT ''GetBool) mode,
ConT ''Bool,
ConT ''SymBool
)
genArgs mode UIntN = do
n <- newName "n"
let nType = VarT n
return
( [kindedTV n (ConT ''Nat)],
[ AppT (ConT ''KnownNat) nType,
AppT (AppT (ConT ''(<=)) (LitT $ NumTyLit 1)) nType
],
AppT (AppT (ConT ''GetIntN) mode) nType,
AppT (ConT ''IntN) nType,
AppT (ConT ''SymIntN) nType
)
genArgs mode UWordN = do
n <- newName "n"
let nType = VarT n
return
( [kindedTV n (ConT ''Nat)],
[ AppT (ConT ''KnownNat) nType,
AppT (AppT (ConT ''(<=)) (LitT $ NumTyLit 1)) nType
],
AppT (AppT (ConT ''GetWordN) mode) nType,
AppT (ConT ''WordN) nType,
AppT (ConT ''SymWordN) nType
)
genArgs mode UInteger =
return
( [],
[],
AppT (ConT ''GetInteger) mode,
ConT ''Integer,
ConT ''SymInteger
)
genArgs mode UAlgReal =
return
( [],
[],
AppT (ConT ''GetAlgReal) mode,
ConT ''AlgReal,
ConT ''SymAlgReal
)
genArgs mode UFP = do
eb <- newName "eb"
sb <- newName "sb"
let ebType = VarT eb
let sbType = VarT sb
return
( [kindedTV eb (ConT ''Nat), kindedTV sb (ConT ''Nat)],
[AppT (AppT (ConT ''ValidFP) ebType) sbType],
AppT (AppT (AppT (ConT ''GetFP) mode) ebType) sbType,
AppT (AppT (ConT ''FP) ebType) sbType,
AppT (AppT (ConT ''SymFP) ebType) sbType
)
genArgs _ UFun {} = fail "UFun cannot be nested."