packages feed

grisette-0.7.0.0: src/Grisette/Internal/TH/DeriveInstanceProvider.hs

{-# LANGUAGE TemplateHaskell #-}

-- |
-- Module      :   Grisette.Internal.TH.DeriveInstanceProvider
-- 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.DeriveInstanceProvider
  ( DeriveInstanceProvider (..),
    Strategy (..),
  )
where

import Generics.Deriving (Default, Default1)
import Language.Haskell.TH
  ( Dec,
    DerivStrategy
      ( AnyclassStrategy,
        NewtypeStrategy,
        StockStrategy,
        ViaStrategy
      ),
    Name,
    Pred,
    Q,
    Type,
    appT,
    conT,
    standaloneDerivWithStrategyD,
  )
import Language.Haskell.TH.Datatype.TyVarBndr (TyVarBndrUnit)

-- | A derive instance provider provides the instance declaration.
class DeriveInstanceProvider provider where
  instanceDeclaration ::
    provider -> [[(TyVarBndrUnit, Maybe Type)]] -> [Pred] -> [Type] -> Q [Dec]

-- | A strategy for deriving instances.
data Strategy
  = Stock {strategyClassName :: Name}
  | WithNewtype {strategyClassName :: Name}
  | ViaDefault {strategyClassName :: Name}
  | ViaDefault1 {strategyClassName :: Name}
  | Anyclass {strategyClassName :: Name}
  deriving (Eq)

getStrategy :: Strategy -> Type -> Q DerivStrategy
getStrategy strategy ty =
  case strategy of
    Stock _ -> return StockStrategy
    WithNewtype _ -> return NewtypeStrategy
    ViaDefault _ ->
      ViaStrategy
        <$> [t|Default $(return ty)|]
    ViaDefault1 _ ->
      ViaStrategy
        <$> [t|Default1 $(return ty)|]
    Anyclass _ -> return AnyclassStrategy

instance DeriveInstanceProvider Strategy where
  instanceDeclaration strategy _ preds tys = do
    s <- getStrategy strategy (last tys)
    (: [])
      <$> standaloneDerivWithStrategyD
        (Just s)
        (return preds)
        (foldl appT (conT $ strategyClassName strategy) $ return <$> tys)