{-# Language CPP, DeriveDataTypeable #-}

#if MIN_VERSION_base(4,4,0)
#define HAS_GENERICS
{-# Language DeriveGeneric #-}
#endif

#if MIN_VERSION_template_haskell(2,12,0)
{-# Language Safe #-}
#elif __GLASGOW_HASKELL__ >= 702
{-# Language Trustworthy #-}
#endif

#if __GLASGOW_HASKELL__ >= 708
{-# Language PatternSynonyms #-}
{-# Language ViewPatterns #-}
#endif

#if __GLASGOW_HASKELL__ >= 800
#define HAS_TH_LIFT
{-# Language DeriveLift #-}
#endif

{-|
Module      : Language.Haskell.TH.Datatype.TyVarBndr
Description : Backwards-compatible type variable binders
Copyright   : Eric Mertens 2020
License     : ISC
Maintainer  : emertens@gmail.com

This module provides a backwards-compatible API for constructing and
manipulating 'TyVarBndr's across multiple versions of the @template-haskell@
package.

-}
module Language.Haskell.TH.Datatype.TyVarBndr (
    -- * @TyVarBndr@-related types
    TyVarBndr_
  , TyVarBndrUnit
  , TyVarBndrSpec
  , TyVarBndrVis
  , Specificity(..)
#if __GLASGOW_HASKELL__ >= 907
  , BndrVis(..)
#elif __GLASGOW_HASKELL__ >= 708
  , BndrVis
  , pattern BndrReq
  , pattern BndrInvis
#else
  , BndrVis
#endif
  , DefaultBndrFlag(..)

    -- * Constructing @TyVarBndr@s
    -- ** @flag@-polymorphic
  , plainTVFlag
  , kindedTVFlag
    -- ** @TyVarBndrUnit@
  , plainTV
  , kindedTV
    -- ** @TyVarBndrSpec@
  , plainTVInferred
  , plainTVSpecified
  , kindedTVInferred
  , kindedTVSpecified
    -- ** @TyVarBndrVis@
  , plainTVReq
  , plainTVInvis
  , kindedTVReq
  , kindedTVInvis

    -- * Constructing @Specificity@
  , inferredSpec
  , specifiedSpec

    -- * Constructing @BndrVis@
  , bndrReq
  , bndrInvis

    -- * Modifying @TyVarBndr@s
  , elimTV
  , elimTVFlag
  , mapTV
  , mapTVName
  , mapTVFlag
  , mapTVKind
  , traverseTV
  , traverseTVName
  , traverseTVFlag
  , traverseTVKind
  , mapMTV
  , mapMTVName
  , mapMTVFlag
  , mapMTVKind
  , changeTVFlags

    -- * Properties of @TyVarBndr@s
  , tvName
  , tvKind
  , tvFlag
  ) where

import Control.Applicative
import Control.Monad
import Data.Data (Typeable, Data)
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Syntax

#ifdef HAS_GENERICS
import GHC.Generics (Generic)
#endif

-- | A type synonym for 'TyVarBndr'. This is the recommended way to refer to
-- 'TyVarBndr's if you wish to achieve backwards compatibility with older
-- versions of @template-haskell@, where 'TyVarBndr' lacked a @flag@ type
-- parameter (if it has one).
#if MIN_VERSION_template_haskell(2,17,0)
type TyVarBndr_ flag = TyVarBndr flag
#else
type TyVarBndr_ flag = TyVarBndr

-- | A 'TyVarBndr' without a flag. This is used for 'TyVarBndr's that do not
-- interact with visible type application and are not binders for type-level
-- declarations.
type TyVarBndrUnit = TyVarBndr

-- | A 'TyVarBndr' with a 'Specificity' flag. This is used for 'TyVarBndr's that
-- interact with visible type application.
type TyVarBndrSpec = TyVarBndr

-- | Determines how a 'TyVarBndr' interacts with visible type application.
data Specificity
  = SpecifiedSpec -- ^ @a@. Eligible for visible type application.
  | InferredSpec  -- ^ @{a}@. Not eligible for visible type application.
  deriving (Show, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
           ,Generic
#endif
#ifdef HAS_TH_LIFT
           ,Lift
#endif
           )

inferredSpec :: Specificity
inferredSpec = InferredSpec

specifiedSpec :: Specificity
specifiedSpec = SpecifiedSpec
#endif

#if !MIN_VERSION_template_haskell(2,21,0)
-- | A 'TyVarBndr' with a 'BndrVis' flag. This is used for 'TyVarBndr's in
-- type-level declarations (e.g., the binders in @data D \@k (a :: k)@).
type TyVarBndrVis = TyVarBndr_ BndrVis

-- | Because pre-9.8 GHCs do not support invisible binders in type-level
-- declarations, we simply make 'BndrVis' an alias for @()@ as a compatibility
-- shim for old GHCs. This matches how type-level 'TyVarBndr's were flagged
-- prior to GHC 9.8.
type BndrVis = ()
#if __GLASGOW_HASKELL__ >= 802
{-# COMPLETE BndrReq, BndrInvis #-}
#endif

#if __GLASGOW_HASKELL__ >= 708
-- | Because pre-9.8 GHCs do not support invisible binders in type-level
-- declarations, we simply make 'BndrReq' a pattern synonym for @()@ as a
-- compatibility shim for old GHCs. This matches how type-level 'TyVarBndr's
-- were flagged prior to GHC 9.8.
#if __GLASGOW_HASKELL__ >= 800
pattern BndrReq :: BndrVis
#endif
pattern $mBndrReq :: forall {r}. BndrVis -> ((# #) -> r) -> ((# #) -> r) -> r
$bBndrReq :: BndrVis
BndrReq = ()

-- | Because pre-9.8 GHCs do not support invisible binders in type-level
-- declarations, this compatibility shim is defined in a somewhat unusual way:
--
-- * As a pattern, 'BndrInvis' will never match on pre-9.8 GHCs. That way, if
--   you write pattern matches like this:
--
--   @
--   case flag of
--     'BndrInvis' -> ...
--     'BndrVis' -> ...
--   @
--
--   Then the first branch will never be taken on pre-9.8 GHCs.
--
-- * 'BndrInvis' is a unidirectional pattern synonym on pre-9.8 GHCs, so it
--   cannot be used as an expression on these GHC versions. This is done in an
--   effort to avoid pitfalls that could occur if 'BndrInvis' were defined like
--   so:
--
--   @
--   pattern 'BndrInvis' = ()
--   @
--
--   If this were the definition, then a user could write code involving
--   'BndrInvis' that would construct an invisible type-level binder on GHC 9.8
--   or later, but a /visible/ type-level binder on older GHCs! This would be
--   disastrous, so we prevent the user from doing such a thing.
#if __GLASGOW_HASKELL__ >= 800
pattern BndrInvis :: BndrVis
#endif
pattern $mBndrInvis :: forall {r}. BndrVis -> ((# #) -> r) -> ((# #) -> r) -> r
BndrInvis <- ((\() -> Bool
True) -> False)
#endif

bndrReq :: BndrVis
bndrReq :: BndrVis
bndrReq = ()

bndrInvis :: BndrVis
bndrInvis :: BndrVis
bndrInvis = ()

-- | A class characterizing reasonable default values for various 'TyVarBndr'
-- @flag@ types.
class DefaultBndrFlag flag where
  defaultBndrFlag :: flag

instance DefaultBndrFlag () where
  defaultBndrFlag :: BndrVis
defaultBndrFlag = ()

instance DefaultBndrFlag Specificity where
  defaultBndrFlag :: Specificity
defaultBndrFlag = Specificity
SpecifiedSpec
#endif

-- | Construct a 'PlainTV' with the given @flag@.
plainTVFlag :: Name -> flag -> TyVarBndr_ flag
#if MIN_VERSION_template_haskell(2,17,0)
plainTVFlag :: forall flag. Name -> flag -> TyVarBndr_ flag
plainTVFlag = Name -> flag -> TyVarBndr flag
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV
#else
plainTVFlag n _ = PlainTV n
#endif

-- | Construct a 'PlainTV' with an 'InferredSpec'.
plainTVInferred :: Name -> TyVarBndrSpec
plainTVInferred :: Name -> TyVarBndrSpec
plainTVInferred Name
n = Name -> Specificity -> TyVarBndrSpec
forall flag. Name -> flag -> TyVarBndr_ flag
plainTVFlag Name
n Specificity
InferredSpec

-- | Construct a 'PlainTV' with a 'SpecifiedSpec'.
plainTVSpecified :: Name -> TyVarBndrSpec
plainTVSpecified :: Name -> TyVarBndrSpec
plainTVSpecified Name
n = Name -> Specificity -> TyVarBndrSpec
forall flag. Name -> flag -> TyVarBndr_ flag
plainTVFlag Name
n Specificity
SpecifiedSpec

-- | Construct a 'PlainTV' with a 'BndrReq'.
plainTVReq :: Name -> TyVarBndrVis
plainTVReq :: Name -> TyVarBndrVis
plainTVReq Name
n = Name -> BndrVis -> TyVarBndrVis
forall flag. Name -> flag -> TyVarBndr_ flag
plainTVFlag Name
n BndrVis
bndrReq

-- | Construct a 'PlainTV' with a 'BndrInvis'.
plainTVInvis :: Name -> TyVarBndrVis
plainTVInvis :: Name -> TyVarBndrVis
plainTVInvis Name
n = Name -> BndrVis -> TyVarBndrVis
forall flag. Name -> flag -> TyVarBndr_ flag
plainTVFlag Name
n BndrVis
bndrInvis

-- | Construct a 'KindedTV' with the given @flag@.
kindedTVFlag :: Name -> flag -> Kind -> TyVarBndr_ flag
#if MIN_VERSION_template_haskell(2,17,0)
kindedTVFlag :: forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
kindedTVFlag = Name -> flag -> Kind -> TyVarBndr flag
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV
#else
kindedTVFlag n _ kind = KindedTV n kind
#endif

-- | Construct a 'KindedTV' with an 'InferredSpec'.
kindedTVInferred :: Name -> Kind -> TyVarBndrSpec
kindedTVInferred :: Name -> Kind -> TyVarBndrSpec
kindedTVInferred Name
n Kind
k = Name -> Specificity -> Kind -> TyVarBndrSpec
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
kindedTVFlag Name
n Specificity
InferredSpec Kind
k

-- | Construct a 'KindedTV' with a 'SpecifiedSpec'.
kindedTVSpecified :: Name -> Kind -> TyVarBndrSpec
kindedTVSpecified :: Name -> Kind -> TyVarBndrSpec
kindedTVSpecified Name
n Kind
k = Name -> Specificity -> Kind -> TyVarBndrSpec
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
kindedTVFlag Name
n Specificity
SpecifiedSpec Kind
k

-- | Construct a 'KindedTV' with a 'BndrReq'.
kindedTVReq :: Name -> Kind -> TyVarBndrVis
kindedTVReq :: Name -> Kind -> TyVarBndrVis
kindedTVReq Name
n Kind
k = Name -> BndrVis -> Kind -> TyVarBndrVis
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
kindedTVFlag Name
n BndrVis
bndrReq Kind
k

-- | Construct a 'KindedTV' with a 'BndrInvis'.
kindedTVInvis :: Name -> Kind -> TyVarBndrVis
kindedTVInvis :: Name -> Kind -> TyVarBndrVis
kindedTVInvis Name
n Kind
k = Name -> BndrVis -> Kind -> TyVarBndrVis
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
kindedTVFlag Name
n BndrVis
bndrInvis Kind
k

-- | Case analysis for a 'TyVarBndr'. If the value is a @'PlainTV' n _@, apply
-- the first function to @n@; if it is @'KindedTV' n _ k@, apply the second
-- function to @n@ and @k@.
elimTV :: (Name -> r) -> (Name -> Kind -> r) -> TyVarBndr_ flag -> r
#if MIN_VERSION_template_haskell(2,17,0)
elimTV :: forall r flag.
(Name -> r) -> (Name -> Kind -> r) -> TyVarBndr_ flag -> r
elimTV Name -> r
ptv Name -> Kind -> r
_ktv (PlainTV Name
n flag
_)    = Name -> r
ptv Name
n
elimTV Name -> r
_ptv Name -> Kind -> r
ktv (KindedTV Name
n flag
_ Kind
k) = Name -> Kind -> r
ktv Name
n Kind
k
#else
elimTV ptv _ktv (PlainTV n)    = ptv n
elimTV _ptv ktv (KindedTV n k) = ktv n k
#endif

-- | Case analysis for a 'TyVarBndr' that includes @flag@s in the continuation
-- arguments. Note that 'TyVarBndr's did not include @flag@s prior to
-- @template-haskell-2.17.0.0@, so on older versions of @template-haskell@,
-- these @flag@s instead become @()@.
#if MIN_VERSION_template_haskell(2,17,0)
elimTVFlag :: (Name -> flag -> r) -> (Name -> flag -> Kind -> r) -> TyVarBndr_ flag -> r
elimTVFlag :: forall flag r.
(Name -> flag -> r)
-> (Name -> flag -> Kind -> r) -> TyVarBndr_ flag -> r
elimTVFlag Name -> flag -> r
ptv Name -> flag -> Kind -> r
_ktv (PlainTV Name
n flag
flag)    = Name -> flag -> r
ptv Name
n flag
flag
elimTVFlag Name -> flag -> r
_ptv Name -> flag -> Kind -> r
ktv (KindedTV Name
n flag
flag Kind
k) = Name -> flag -> Kind -> r
ktv Name
n flag
flag Kind
k
#else
elimTVFlag :: (Name -> () -> r) -> (Name -> () -> Kind -> r) -> TyVarBndr_ flag -> r
elimTVFlag ptv _ktv (PlainTV n)    = ptv n ()
elimTVFlag _ptv ktv (KindedTV n k) = ktv n () k
#endif

-- | Map over the components of a 'TyVarBndr'.
mapTV :: (Name -> Name) -> (flag -> flag') -> (Kind -> Kind)
      -> TyVarBndr_ flag -> TyVarBndr_ flag'
#if MIN_VERSION_template_haskell(2,17,0)
mapTV :: forall flag flag'.
(Name -> Name)
-> (flag -> flag')
-> (Kind -> Kind)
-> TyVarBndr_ flag
-> TyVarBndr_ flag'
mapTV Name -> Name
fn flag -> flag'
fflag Kind -> Kind
_fkind (PlainTV  Name
n flag
flag)      = Name -> flag' -> TyVarBndr flag'
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV  (Name -> Name
fn Name
n) (flag -> flag'
fflag flag
flag)
mapTV Name -> Name
fn flag -> flag'
fflag  Kind -> Kind
fkind (KindedTV Name
n flag
flag Kind
kind) = Name -> flag' -> Kind -> TyVarBndr flag'
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV (Name -> Name
fn Name
n) (flag -> flag'
fflag flag
flag) (Kind -> Kind
fkind Kind
kind)
#else
mapTV fn _fflag _fkind (PlainTV  n)      = PlainTV  (fn n)
mapTV fn _fflag  fkind (KindedTV n kind) = KindedTV (fn n) (fkind kind)
#endif

-- | Map over the 'Name' of a 'TyVarBndr'.
mapTVName :: (Name -> Name) -> TyVarBndr_ flag -> TyVarBndr_ flag
mapTVName :: forall flag. (Name -> Name) -> TyVarBndr_ flag -> TyVarBndr_ flag
mapTVName Name -> Name
fname = (Name -> Name)
-> (flag -> flag)
-> (Kind -> Kind)
-> TyVarBndr_ flag
-> TyVarBndr_ flag
forall flag flag'.
(Name -> Name)
-> (flag -> flag')
-> (Kind -> Kind)
-> TyVarBndr_ flag
-> TyVarBndr_ flag'
mapTV Name -> Name
fname flag -> flag
forall a. a -> a
id Kind -> Kind
forall a. a -> a
id

-- | Map over the @flag@ of a 'TyVarBndr'.
mapTVFlag :: (flag -> flag') -> TyVarBndr_ flag -> TyVarBndr_ flag'
#if MIN_VERSION_template_haskell(2,17,0)
mapTVFlag :: forall flag flag'.
(flag -> flag') -> TyVarBndr_ flag -> TyVarBndr_ flag'
mapTVFlag = (flag -> flag') -> TyVarBndr flag -> TyVarBndr flag'
forall flag flag'.
(flag -> flag') -> TyVarBndr_ flag -> TyVarBndr_ flag'
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
#else
mapTVFlag _ = id
#endif

-- | Map over the 'Kind' of a 'TyVarBndr'.
mapTVKind :: (Kind -> Kind) -> TyVarBndr_ flag -> TyVarBndr_ flag
mapTVKind :: forall flag. (Kind -> Kind) -> TyVarBndr_ flag -> TyVarBndr_ flag
mapTVKind Kind -> Kind
fkind = (Name -> Name)
-> (flag -> flag)
-> (Kind -> Kind)
-> TyVarBndr_ flag
-> TyVarBndr_ flag
forall flag flag'.
(Name -> Name)
-> (flag -> flag')
-> (Kind -> Kind)
-> TyVarBndr_ flag
-> TyVarBndr_ flag'
mapTV Name -> Name
forall a. a -> a
id flag -> flag
forall a. a -> a
id Kind -> Kind
fkind

-- | Traverse the components of a 'TyVarBndr'.
traverseTV :: Applicative f
           => (Name -> f Name) -> (flag -> f flag') -> (Kind -> f Kind)
           -> TyVarBndr_ flag -> f (TyVarBndr_ flag')
#if MIN_VERSION_template_haskell(2,17,0)
traverseTV :: forall (f :: * -> *) flag flag'.
Applicative f =>
(Name -> f Name)
-> (flag -> f flag')
-> (Kind -> f Kind)
-> TyVarBndr_ flag
-> f (TyVarBndr_ flag')
traverseTV Name -> f Name
fn flag -> f flag'
fflag Kind -> f Kind
_fkind (PlainTV Name
n flag
flag) =
  (Name -> flag' -> TyVarBndr_ flag')
-> f Name -> f flag' -> f (TyVarBndr_ flag')
forall a b c. (a -> b -> c) -> f a -> f b -> f c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 Name -> flag' -> TyVarBndr_ flag'
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV (Name -> f Name
fn Name
n) (flag -> f flag'
fflag flag
flag)
traverseTV Name -> f Name
fn flag -> f flag'
fflag Kind -> f Kind
fkind (KindedTV Name
n flag
flag Kind
kind) =
  (Name -> flag' -> Kind -> TyVarBndr_ flag')
-> f Name -> f flag' -> f Kind -> f (TyVarBndr_ flag')
forall (f :: * -> *) a b c d.
Applicative f =>
(a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 Name -> flag' -> Kind -> TyVarBndr_ flag'
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV (Name -> f Name
fn Name
n) (flag -> f flag'
fflag flag
flag) (Kind -> f Kind
fkind Kind
kind)
#else
traverseTV fn _fflag _fkind (PlainTV n) =
  PlainTV <$> fn n
traverseTV fn _fflag fkind (KindedTV n kind) =
  liftA2 KindedTV (fn n) (fkind kind)
#endif

-- | Traverse the 'Name' of a 'TyVarBndr'.
traverseTVName :: Functor f
               => (Name -> f Name)
               -> TyVarBndr_ flag -> f (TyVarBndr_ flag)
#if MIN_VERSION_template_haskell(2,17,0)
traverseTVName :: forall (f :: * -> *) flag.
Functor f =>
(Name -> f Name) -> TyVarBndr_ flag -> f (TyVarBndr_ flag)
traverseTVName Name -> f Name
fn (PlainTV Name
n flag
flag) =
  (\Name
n' -> Name -> flag -> TyVarBndr flag
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV Name
n' flag
flag) (Name -> TyVarBndr flag) -> f Name -> f (TyVarBndr flag)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Name -> f Name
fn Name
n
traverseTVName Name -> f Name
fn (KindedTV Name
n flag
flag Kind
kind) =
  (\Name
n' -> Name -> flag -> Kind -> TyVarBndr flag
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n' flag
flag Kind
kind) (Name -> TyVarBndr flag) -> f Name -> f (TyVarBndr flag)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Name -> f Name
fn Name
n
#else
traverseTVName fn (PlainTV n) =
  PlainTV <$> fn n
traverseTVName fn (KindedTV n kind) =
  (\n' -> KindedTV n' kind) <$> fn n
#endif

-- | Traverse the @flag@ of a 'TyVarBndr'.
traverseTVFlag :: Applicative f
               => (flag -> f flag')
               -> TyVarBndr_ flag -> f (TyVarBndr_ flag')
#if MIN_VERSION_template_haskell(2,17,0)
traverseTVFlag :: forall (f :: * -> *) flag flag'.
Applicative f =>
(flag -> f flag') -> TyVarBndr_ flag -> f (TyVarBndr_ flag')
traverseTVFlag flag -> f flag'
fflag (PlainTV Name
n flag
flag) =
  Name -> flag' -> TyVarBndr flag'
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV Name
n (flag' -> TyVarBndr flag') -> f flag' -> f (TyVarBndr flag')
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> flag -> f flag'
fflag flag
flag
traverseTVFlag flag -> f flag'
fflag (KindedTV Name
n flag
flag Kind
kind) =
  (\flag'
flag' -> Name -> flag' -> Kind -> TyVarBndr flag'
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n flag'
flag' Kind
kind) (flag' -> TyVarBndr flag') -> f flag' -> f (TyVarBndr flag')
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> flag -> f flag'
fflag flag
flag
#else
traverseTVFlag _ = pure
#endif

-- | Traverse the 'Kind' of a 'TyVarBndr'.
traverseTVKind :: Applicative f
               => (Kind -> f Kind)
               -> TyVarBndr_ flag -> f (TyVarBndr_ flag)
#if MIN_VERSION_template_haskell(2,17,0)
traverseTVKind :: forall (f :: * -> *) flag.
Applicative f =>
(Kind -> f Kind) -> TyVarBndr_ flag -> f (TyVarBndr_ flag)
traverseTVKind Kind -> f Kind
_fkind tvb :: TyVarBndr_ flag
tvb@PlainTV{} =
  TyVarBndr_ flag -> f (TyVarBndr_ flag)
forall a. a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TyVarBndr_ flag
tvb
traverseTVKind Kind -> f Kind
fkind (KindedTV Name
n flag
flag Kind
kind) =
  Name -> flag -> Kind -> TyVarBndr_ flag
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n flag
flag (Kind -> TyVarBndr_ flag) -> f Kind -> f (TyVarBndr_ flag)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Kind -> f Kind
fkind Kind
kind
#else
traverseTVKind _fkind tvb@PlainTV{} =
  pure tvb
traverseTVKind fkind (KindedTV n kind) =
  KindedTV n <$> fkind kind
#endif

-- | Map over the components of a 'TyVarBndr' in a monadic fashion.
--
-- This is the same as 'traverseTV', but with a 'Monad' constraint. This is
-- mainly useful for use with old versions of @base@ where 'Applicative' was
-- not a superclass of 'Monad'.
mapMTV :: Monad m
       => (Name -> m Name) -> (flag -> m flag') -> (Kind -> m Kind)
       -> TyVarBndr_ flag -> m (TyVarBndr_ flag')
#if MIN_VERSION_template_haskell(2,17,0)
mapMTV :: forall (m :: * -> *) flag flag'.
Monad m =>
(Name -> m Name)
-> (flag -> m flag')
-> (Kind -> m Kind)
-> TyVarBndr_ flag
-> m (TyVarBndr_ flag')
mapMTV Name -> m Name
fn flag -> m flag'
fflag Kind -> m Kind
_fkind (PlainTV Name
n flag
flag) =
  (Name -> flag' -> TyVarBndr_ flag')
-> m Name -> m flag' -> m (TyVarBndr_ flag')
forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 Name -> flag' -> TyVarBndr_ flag'
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV (Name -> m Name
fn Name
n) (flag -> m flag'
fflag flag
flag)
mapMTV Name -> m Name
fn flag -> m flag'
fflag Kind -> m Kind
fkind (KindedTV Name
n flag
flag Kind
kind) =
  (Name -> flag' -> Kind -> TyVarBndr_ flag')
-> m Name -> m flag' -> m Kind -> m (TyVarBndr_ flag')
forall (m :: * -> *) a1 a2 a3 r.
Monad m =>
(a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
liftM3 Name -> flag' -> Kind -> TyVarBndr_ flag'
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV (Name -> m Name
fn Name
n) (flag -> m flag'
fflag flag
flag) (Kind -> m Kind
fkind Kind
kind)
#else
mapMTV fn _fflag _fkind (PlainTV n) =
  liftM PlainTV (fn n)
mapMTV fn _fflag fkind (KindedTV n kind) =
  liftM2 KindedTV (fn n) (fkind kind)
#endif

-- | Map over the 'Name' of a 'TyVarBndr' in a monadic fashion.
--
-- This is the same as 'traverseTVName', but with a 'Monad' constraint. This is
-- mainly useful for use with old versions of @base@ where 'Applicative' was
-- not a superclass of 'Monad'.
mapMTVName :: Monad m
           => (Name -> m Name)
           -> TyVarBndr_ flag -> m (TyVarBndr_ flag)
#if MIN_VERSION_template_haskell(2,17,0)
mapMTVName :: forall (m :: * -> *) flag.
Monad m =>
(Name -> m Name) -> TyVarBndr_ flag -> m (TyVarBndr_ flag)
mapMTVName Name -> m Name
fn (PlainTV Name
n flag
flag) =
  (Name -> TyVarBndr flag) -> m Name -> m (TyVarBndr flag)
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (\Name
n' -> Name -> flag -> TyVarBndr flag
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV Name
n' flag
flag) (Name -> m Name
fn Name
n)
mapMTVName Name -> m Name
fn (KindedTV Name
n flag
flag Kind
kind) =
  (Name -> TyVarBndr flag) -> m Name -> m (TyVarBndr flag)
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (\Name
n' -> Name -> flag -> Kind -> TyVarBndr flag
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n' flag
flag Kind
kind) (Name -> m Name
fn Name
n)
#else
mapMTVName fn (PlainTV n) =
  liftM PlainTV (fn n)
mapMTVName fn (KindedTV n kind) =
  liftM (\n' -> KindedTV n' kind) (fn n)
#endif

-- | Map over the @flag@ of a 'TyVarBndr' in a monadic fashion.
--
-- This is the same as 'traverseTVFlag', but with a 'Monad' constraint. This is
-- mainly useful for use with old versions of @base@ where 'Applicative' was
-- not a superclass of 'Monad'.
mapMTVFlag :: Monad m
           => (flag -> m flag')
           -> TyVarBndr_ flag -> m (TyVarBndr_ flag')
#if MIN_VERSION_template_haskell(2,17,0)
mapMTVFlag :: forall (m :: * -> *) flag flag'.
Monad m =>
(flag -> m flag') -> TyVarBndr_ flag -> m (TyVarBndr_ flag')
mapMTVFlag flag -> m flag'
fflag (PlainTV Name
n flag
flag) =
  (flag' -> TyVarBndr_ flag') -> m flag' -> m (TyVarBndr_ flag')
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (Name -> flag' -> TyVarBndr_ flag'
forall flag. Name -> flag -> TyVarBndr_ flag
PlainTV Name
n) (flag -> m flag'
fflag flag
flag)
mapMTVFlag flag -> m flag'
fflag (KindedTV Name
n flag
flag Kind
kind) =
  (flag' -> TyVarBndr_ flag') -> m flag' -> m (TyVarBndr_ flag')
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (\flag'
flag' -> Name -> flag' -> Kind -> TyVarBndr_ flag'
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n flag'
flag' Kind
kind) (flag -> m flag'
fflag flag
flag)
#else
mapMTVFlag _ = return
#endif

-- | Map over the 'Kind' of a 'TyVarBndr' in a monadic fashion.
--
-- This is the same as 'traverseTVKind', but with a 'Monad' constraint. This is
-- mainly useful for use with old versions of @base@ where 'Applicative' was
-- not a superclass of 'Monad'.
mapMTVKind :: Monad m
           => (Kind -> m Kind)
           -> TyVarBndr_ flag -> m (TyVarBndr_ flag)
#if MIN_VERSION_template_haskell(2,17,0)
mapMTVKind :: forall (m :: * -> *) flag.
Monad m =>
(Kind -> m Kind) -> TyVarBndr_ flag -> m (TyVarBndr_ flag)
mapMTVKind Kind -> m Kind
_fkind tvb :: TyVarBndr_ flag
tvb@PlainTV{} =
  TyVarBndr_ flag -> m (TyVarBndr_ flag)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return TyVarBndr_ flag
tvb
mapMTVKind Kind -> m Kind
fkind (KindedTV Name
n flag
flag Kind
kind) =
  (Kind -> TyVarBndr_ flag) -> m Kind -> m (TyVarBndr_ flag)
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (Name -> flag -> Kind -> TyVarBndr_ flag
forall flag. Name -> flag -> Kind -> TyVarBndr_ flag
KindedTV Name
n flag
flag) (Kind -> m Kind
fkind Kind
kind)
#else
mapMTVKind _fkind tvb@PlainTV{} =
  return tvb
mapMTVKind fkind (KindedTV n kind) =
  liftM (KindedTV n) (fkind kind)
#endif

-- | Set the flag in a list of 'TyVarBndr's. This is often useful in contexts
-- where one needs to re-use a list of 'TyVarBndr's from one flag setting to
-- another flag setting. For example, in order to re-use the 'TyVarBndr's bound
-- by a 'DataD' in a 'ForallT', one can do the following:
--
-- @
-- case x of
--   'DataD' _ _ tvbs _ _ _ ->
--     'ForallT' ('changeTVFlags' 'SpecifiedSpec' tvbs) ...
-- @
changeTVFlags :: newFlag -> [TyVarBndr_ oldFlag] -> [TyVarBndr_ newFlag]
#if MIN_VERSION_template_haskell(2,17,0)
changeTVFlags :: forall newFlag oldFlag.
newFlag -> [TyVarBndr_ oldFlag] -> [TyVarBndr_ newFlag]
changeTVFlags newFlag
newFlag = (TyVarBndr_ oldFlag -> TyVarBndr_ newFlag)
-> [TyVarBndr_ oldFlag] -> [TyVarBndr_ newFlag]
forall a b. (a -> b) -> [a] -> [b]
map (newFlag
newFlag newFlag -> TyVarBndr_ oldFlag -> TyVarBndr_ newFlag
forall a b. a -> TyVarBndr b -> TyVarBndr a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$)
#else
changeTVFlags _ = id
#endif

-- | Extract the type variable name from a 'TyVarBndr', ignoring the
-- kind signature if one exists.
tvName :: TyVarBndr_ flag -> Name
tvName :: forall flag. TyVarBndr_ flag -> Name
tvName = (Name -> Name) -> (Name -> Kind -> Name) -> TyVarBndr_ flag -> Name
forall r flag.
(Name -> r) -> (Name -> Kind -> r) -> TyVarBndr_ flag -> r
elimTV Name -> Name
forall a. a -> a
id (\Name
n Kind
_ -> Name
n)

-- | Extract the kind from a 'TyVarBndr'. Assumes 'PlainTV' has kind @*@.
tvKind :: TyVarBndr_ flag -> Kind
tvKind :: forall flag. TyVarBndr_ flag -> Kind
tvKind = (Name -> Kind) -> (Name -> Kind -> Kind) -> TyVarBndr_ flag -> Kind
forall r flag.
(Name -> r) -> (Name -> Kind -> r) -> TyVarBndr_ flag -> r
elimTV (\Name
_ -> Kind
starK) (\Name
_ Kind
k -> Kind
k)

-- | Extract the @flag@ from a 'TyVarBndr'. Note that 'TyVarBndr's did not
-- include @flag@s prior to @template-haskell-2.17.0.0@, so on older versions of
-- @template-haskell@, this functions instead returns @()@.
#if MIN_VERSION_template_haskell(2,17,0)
tvFlag :: TyVarBndr_ flag -> flag
tvFlag :: forall flag. TyVarBndr_ flag -> flag
tvFlag = (Name -> flag -> flag)
-> (Name -> flag -> Kind -> flag) -> TyVarBndr_ flag -> flag
forall flag r.
(Name -> flag -> r)
-> (Name -> flag -> Kind -> r) -> TyVarBndr_ flag -> r
elimTVFlag (\Name
_ flag
flag -> flag
flag) (\Name
_ flag
flag Kind
_ -> flag
flag)
#else
tvFlag :: TyVarBndr_ flag -> ()
tvFlag _ = ()
#endif