th-utilities-0.1.1.0: src/TH/Derive/Internal.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE KindSignatures #-}
module TH.Derive.Internal (Deriving, Deriver(..), Instantiator(..)) where
import Data.Proxy (Proxy)
import GHC.Exts (Constraint)
import Language.Haskell.TH (Q, Dec, Cxt, Type)
-- | This class has no instances. Its only purpose is usage within the
-- @[d| ... |]@ quote provided to 'derive'. Usage such as @instance
-- Deriving (Foo X)@ indicates that you would like to use the 'Deriver'
-- registered for @Foo a@.
class Deriving (cls :: Constraint) where
-- Un-exported method, to prevent this class from being
-- instantiated.
_noInstances :: Proxy cls
-- | Instances of 'Deriver' describe a default way of creating an
-- instance for a particular typeclass. For example, if I wanted to
-- write something that derives 'Eq' instances, I would write a
-- @instance Deriver (Eq a)@.
class Deriver (cls :: Constraint) where
runDeriver :: Proxy cls -> Cxt -> Type -> Q [Dec]
-- | Instances of 'Instantiator' are similar in purpose to instance of
-- 'Deriver'. The difference is that instead of using the 'Deriving'
-- class, each instantiator has its own new typeclass. This means that
-- you can have multiple instantiators that all produce instances for
-- the same typeclass, using different approaches.
--
-- Having a new class also allows the instantiator to have methods and
-- data / type family declarations. This allows the user to provide
-- definitions which specify how the generated instances behave. For
-- example, lets say we want to be able to directly define 'Eq' and
-- 'Ord' instances via a conversion function to the type to compare.
-- Here's what this currently looks like:
--
-- @
-- class Ord o => InstEqOrdVia o a where
-- _toOrd :: a -> o
--
-- instance Instantiator (InstEqOrdVia o a) where
-- runInstantiator _ preds (AppT (AppT (ConT ((== ''InstEqOrdVia) -> True)) _oTy) aTy) decls =
-- dequalifyMethods ''InstEqOrdVia =<<
-- sequence
-- [instanceD (return preds) [t| Eq $(return aTy) |] $
-- [valD (varP '(==))
-- (normalB [| \l r -> _toOrd l == _toOrd r |])
-- (map return decls)]
-- , instanceD (return preds) [t| Ord $(return aTy) |] $
-- [valD (varP 'compare)
-- (normalB [| \l r -> compare (_toOrd l) (_toOrd r) |])
-- (map return decls)
-- ]
-- ]
-- runInstantiator _ _ _ _ =
-- fail "Theoretically impossible case in InstEqOrdVia instantiator"
-- @
--
-- Why the underscore prefixing of @_toOrd@? It's to suppress name
-- shadowing warnings which otherwise occur. In the future, this library
-- will likely provide pretty ways to define instantiators. For now it's
-- a bit ugly.
--
-- Here's what usage of this looks like:
--
-- @
-- data T = Y | Z
--
-- $($(derive [d|
-- instance InstEqOrdVia Bool T where
-- _toOrd Y = True
-- _toOrd Z = False
-- |]))
--
-- main = when (Y > Z) (putStrLn "It worked!!")
-- @
class Instantiator (inst :: Constraint) where
runInstantiator :: Proxy inst -> Cxt -> Type -> [Dec] -> Q [Dec]