classy-effects-th-0.1.0.0: src/Control/Effect/Class/Machinery/TH/Send.hs
{-# LANGUAGE TemplateHaskell #-}
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
{- |
Copyright : (c) 2023 Yamada Ryo
License : MPL-2.0 (see the file LICENSE)
Maintainer : ymdfield@outlook.jp
Stability : experimental
Portability : portable
This module provides @TemplateHaskell@ functions to derive an instance of the effect that handles
via 'SendIns'/'SendSig' type classes.
-}
module Control.Effect.Class.Machinery.TH.Send where
import Control.Effect.Class (EffectDataHandler, EffectsVia, SendIns, SendSig)
import Control.Effect.Class.Machinery.TH.Send.Internal (effectMethodDec)
import Control.Exception (assert)
import Control.Monad (forM)
import Data.Effect.Class.TH.HFunctor.Internal (tyVarName)
import Data.Effect.Class.TH.Internal (
EffectInfo,
EffectOrder (FirstOrder, HigherOrder),
MethodInterface (MethodInterface, methodName),
effMethods,
effMonad,
effName,
effParamVars,
effectParamCxt,
reifyEffectInfo,
renameMethodToCon,
superEffects,
tyVarType,
unkindTyVar,
)
import Data.Maybe (maybeToList)
import Language.Haskell.TH (
Dec (InstanceD),
Name,
Q,
Type (ConT),
appT,
conT,
varT,
)
-- | Derive an instance of the effect that handles via 'SendIns'/'SendSig' type classes.
makeEffectSend ::
-- | The class name of the effect.
Name ->
-- | The name and order of effect data type corresponding to the effect.
Maybe (EffectOrder, Name) ->
Q [Dec]
makeEffectSend effClsName effDataNameAndOrder = do
info <- reifyEffectInfo effClsName
sequence [deriveEffectSend info effDataNameAndOrder]
{- |
Derive an instance of the effect that handles via 'SendIns'/'SendSig' type classes, from
'EffectInfo'.
-}
deriveEffectSend ::
-- | The reified information of the effect class.
EffectInfo ->
-- | The name and order of effect data type corresponding to the effect.
Maybe (EffectOrder, Name) ->
Q Dec
deriveEffectSend info effDataNameAndOrder = do
let f = varT $ tyVarName $ effMonad info
pvs = effParamVars info
paramTypes = fmap (tyVarType . unkindTyVar) pvs
carrier = [t|EffectsVia EffectDataHandler $f|]
methods =
[ (sig, renameMethodToCon methodName)
| sig@MethodInterface{methodName} <- effMethods info
]
sendCxt <-
maybeToList
<$> forM effDataNameAndOrder \(order, effDataName) ->
assert (not $ null methods) do
let sendCls =
conT case order of
FirstOrder -> ''SendIns
HigherOrder -> ''SendSig
effData = foldl appT (conT effDataName) paramTypes
[t|$sendCls $effData $f|]
let effParamCxt = effectParamCxt info
superEffCxt <- forM (superEffects info) ((`appT` carrier) . pure)
effDataC <- do
let eff = pure $ ConT $ effName info
[t|$(foldl appT eff paramTypes) $carrier|]
decs <- mapM (uncurry $ effectMethodDec $ tyVarName <$> pvs) methods
return $ InstanceD Nothing (sendCxt ++ superEffCxt ++ effParamCxt) effDataC (concat decs)