unbound-generics-0.3: src/Unbound/Generics/LocallyNameless/Alpha.hs
{-# OPTIONS_HADDOCK show-extensions #-}
-- |
-- Module : Unbound.Generics.LocallyNameless.Alpha
-- Copyright : (c) 2014, Aleksey Kliger
-- License : BSD3 (See LICENSE)
-- Maintainer : Aleksey Kliger
-- Stability : experimental
--
-- Use the 'Alpha' typeclass to mark types that may contain 'Name's.
{-# LANGUAGE DefaultSignatures
, FlexibleContexts
, TypeOperators
, RankNTypes
#-}
module Unbound.Generics.LocallyNameless.Alpha (
-- * Name-aware opertions
Alpha(..)
-- * Binder variables
, DisjointSet(..)
, inconsistentDisjointSet
, singletonDisjointSet
, isConsistentDisjointSet
, isNullDisjointSet
-- * Implementation details
, NthPatFind(..)
, NamePatFind(..)
, AlphaCtx
, initialCtx
, patternCtx
, termCtx
, isTermCtx
, incrLevelCtx
, decrLevelCtx
, isZeroLevelCtx
-- * Internal
, gaeq
, gfvAny
, gclose
, gopen
, gisPat
, gisTerm
, gnthPatFind
, gnamePatFind
, gswaps
, gfreshen
, glfreshen
, gacompare
-- ** Interal helpers for gfreshen
, FFM
, liftFFM
, retractFFM
) where
import Control.Applicative (Applicative(..), (<$>))
import Control.Arrow (first)
import Control.Monad (liftM)
import Data.Function (on)
import Data.Functor.Contravariant (Contravariant(..))
import Data.Foldable (Foldable(..))
import Data.List (intersect)
import Data.Monoid (Monoid(..), (<>), All(..))
import Data.Ratio (Ratio)
import Data.Typeable (Typeable, gcast, typeOf)
import GHC.Generics
import Unbound.Generics.LocallyNameless.Name
import Unbound.Generics.LocallyNameless.Fresh
import Unbound.Generics.LocallyNameless.LFresh
import Unbound.Generics.PermM
-- | Some 'Alpha' operations need to record information about their
-- progress. Instances should just pass it through unchanged.
--
-- The context records whether we are currently operating on terms or patterns,
-- and how many binding levels we've descended.
data AlphaCtx = AlphaCtx { ctxMode :: !Mode, ctxLevel :: !Integer }
data Mode = Term | Pat
deriving Eq
-- | The starting context for alpha operations: we are expecting to
-- work on terms and we are under no binders.
initialCtx :: AlphaCtx
initialCtx = AlphaCtx { ctxMode = Term, ctxLevel = 0 }
-- | Switches to a context where we expect to operate on patterns.
patternCtx :: AlphaCtx -> AlphaCtx
patternCtx ctx = ctx { ctxMode = Pat }
-- | Switches to a context where we expect to operate on terms.
termCtx :: AlphaCtx -> AlphaCtx
termCtx ctx = ctx { ctxMode = Term }
-- | Returns 'True' iff we are in a context where we expect to see terms.
isTermCtx :: AlphaCtx -> Bool
isTermCtx (AlphaCtx {ctxMode = Term}) = True
isTermCtx _ = False
-- | Increment the number of binders that we are operating under.
incrLevelCtx :: AlphaCtx -> AlphaCtx
incrLevelCtx ctx = ctx { ctxLevel = 1 + ctxLevel ctx }
-- | Decrement the number of binders that we are operating under.
decrLevelCtx :: AlphaCtx -> AlphaCtx
decrLevelCtx ctx = ctx { ctxLevel = ctxLevel ctx - 1 }
-- | Are we operating under no binders?
isZeroLevelCtx :: AlphaCtx -> Bool
isZeroLevelCtx ctx = ctxLevel ctx == 0
-- | A @DisjointSet a@ is a 'Just' a list of distinct @a@s. In addition to a monoidal
-- structure, a disjoint set also has an annihilator 'inconsistentDisjointSet'.
--
-- @
-- inconsistentDisjointSet \<> s == inconsistentDisjointSet
-- s \<> inconsistentDisjoinSet == inconsistentDisjointSet
-- @
newtype DisjointSet a = DisjointSet (Maybe [a])
instance Eq a => Monoid (DisjointSet a) where
mempty = DisjointSet (Just [])
mappend s1 s2 =
case (s1, s2) of
(DisjointSet (Just xs), DisjointSet (Just ys)) | disjointLists xs ys -> DisjointSet (Just (xs <> ys))
_ -> inconsistentDisjointSet
instance Foldable DisjointSet where
foldMap summarize (DisjointSet ms) = foldMap (foldMap summarize) ms
-- | Returns a @DisjointSet a@ that is the annihilator element for the 'Monoid' instance of 'DisjointSet'.
inconsistentDisjointSet :: DisjointSet a
inconsistentDisjointSet = DisjointSet Nothing
-- | @singletonDisjointSet x@ a @DisjointSet a@ that contains the single element @x@
singletonDisjointSet :: a -> DisjointSet a
singletonDisjointSet x = DisjointSet (Just [x])
disjointLists :: Eq a => [a] -> [a] -> Bool
disjointLists xs ys = null (intersect xs ys)
-- | @isConsistentDisjointSet@ returns @True@ iff the given disjoint set is not inconsistent.
isConsistentDisjointSet :: DisjointSet a -> Bool
isConsistentDisjointSet (DisjointSet Nothing) = False
isConsistentDisjointSet _ = True
-- | @isNullDisjointSet@ return @True@ iff the given disjoint set is 'mempty'.
isNullDisjointSet :: DisjointSet a -> Bool
isNullDisjointSet (DisjointSet (Just [])) = True
isNullDisjointSet _ = False
-- | Types that are instances of @Alpha@ may participate in name representation.
--
-- Minimal instance is entirely empty, provided that your type is an instance of
-- 'Generic'.
class (Show a) => Alpha a where
-- | See 'Unbound.Generics.LocallyNameless.Operations.aeq'.
aeq' :: AlphaCtx -> a -> a -> Bool
default aeq' :: (Generic a, GAlpha (Rep a)) => AlphaCtx -> a -> a -> Bool
aeq' c = (gaeq c) `on` from
{-# INLINE aeq' #-}
-- | See 'Unbound.Generics.LocallyNameless.Operations.fvAny'.
--
-- @
-- fvAny' :: Fold a AnyName
-- @
fvAny' :: (Contravariant f, Applicative f) => AlphaCtx -> (AnyName -> f AnyName) -> a -> f a
default fvAny' :: (Generic a, GAlpha (Rep a), Contravariant f, Applicative f) => AlphaCtx -> (AnyName -> f AnyName) -> a -> f a
fvAny' c nfn = fmap to . gfvAny c nfn . from
{-# INLINE fvAny' #-}
-- | Replace free names by bound names.
close :: AlphaCtx -> NamePatFind -> a -> a
default close :: (Generic a, GAlpha (Rep a)) => AlphaCtx -> NamePatFind -> a -> a
close c b = to . gclose c b . from
{-# INLINE close #-}
-- | Replace bound names by free names.
open :: AlphaCtx -> NthPatFind -> a -> a
default open :: (Generic a, GAlpha (Rep a)) => AlphaCtx -> NthPatFind -> a -> a
open c b = to . gopen c b . from
{-# INLINE open #-}
-- | @isPat x@ dynamically checks whether @x@ can be used as a valid pattern.
isPat :: a -> DisjointSet AnyName
default isPat :: (Generic a, GAlpha (Rep a)) => a -> DisjointSet AnyName
isPat = gisPat . from
{-# INLINE isPat #-}
-- | @isPat x@ dynamically checks whether @x@ can be used as a valid term.
isTerm :: a -> All
default isTerm :: (Generic a, GAlpha (Rep a)) => a -> All
isTerm = gisTerm . from
{-# INLINE isTerm #-}
-- | @isEmbed@ is needed internally for the implementation of
-- 'isPat'. @isEmbed@ is true for terms wrapped in 'Embed' and zero
-- or more occurrences of 'Shift'. The default implementation
-- simply returns @False@.
isEmbed :: a -> Bool
isEmbed _ = False
{-# INLINE isEmbed #-}
-- | If @a@ is a pattern, finds the @n@th name in the pattern
-- (starting from zero), returning the number of names encountered
-- if not found.
nthPatFind :: a -> NthPatFind
default nthPatFind :: (Generic a, GAlpha (Rep a)) => a -> NthPatFind
nthPatFind = gnthPatFind . from
{-# INLINE nthPatFind #-}
-- | If @a@ is a pattern, find the index of the given name in the pattern.
namePatFind :: a -> NamePatFind
default namePatFind :: (Generic a, GAlpha (Rep a)) => a -> NamePatFind
namePatFind = gnamePatFind . from
{-# INLINE namePatFind #-}
-- | See 'Unbound.Generics.LocallyNameless.Operations.swaps'. Apply
-- the given permutation of variable names to the given pattern.
swaps' :: AlphaCtx -> Perm AnyName -> a -> a
default swaps' :: (Generic a, GAlpha (Rep a)) => AlphaCtx -> Perm AnyName -> a -> a
swaps' ctx perm = to . gswaps ctx perm . from
{-# INLINE swaps' #-}
-- | See 'Unbound.Generics.LocallyNameless.Operations.freshen'.
lfreshen' :: LFresh m => AlphaCtx -> a -> (a -> Perm AnyName -> m b) -> m b
default lfreshen' :: (LFresh m, Generic a, GAlpha (Rep a))
=> AlphaCtx -> a -> (a -> Perm AnyName -> m b) -> m b
lfreshen' ctx m cont = glfreshen ctx (from m) (cont . to)
{-# INLINE lfreshen' #-}
-- | See 'Unbound.Generics.LocallyNameless.Operations.freshen'. Rename the free variables
-- in the given term to be distinct from all other names seen in the monad @m@.
freshen' :: Fresh m => AlphaCtx -> a -> m (a, Perm AnyName)
default freshen' :: (Generic a, GAlpha (Rep a), Fresh m) => AlphaCtx -> a -> m (a, Perm AnyName)
freshen' ctx = retractFFM . liftM (first to) . gfreshen ctx . from
-- | See 'Unbound.Generics.LocallyNameless.Operations.acompare'. An alpha-respecting total order on terms involving binders.
acompare' :: AlphaCtx -> a -> a -> Ordering
default acompare' :: (Generic a, GAlpha (Rep a)) => AlphaCtx -> a -> a -> Ordering
acompare' c = (gacompare c) `on` from
-- Internal: the free monad over the Functor f. Note that 'freshen''
-- has a monadic return type and moreover we have to thread the
-- permutation through the 'gfreshen' calls to crawl over the value
-- constructors. Since we don't know anything about the monad @m@,
-- GHC can't help us. But note that none of the code in the generic
-- 'gfreshen' instances actually makes use of the 'Fresh.fresh'
-- function; they just plumb the dictionary through to any 'K' nodes
-- that happen to contain a value of a type like 'Name' that does
-- actually freshen something. So what we do is we actually make
-- gfreshen work not in the monad @m@, but in the monad @FFM m@ and
-- then use 'retractFFM' in the default 'Alpha' method to return back
-- down to @m@. We don't really make use of the fact that 'FFM'
-- reassociates the binds of the underlying monad, but it doesn't hurt
-- anything. Mostly what we care about is giving the inliner a chance
-- to eliminate most of the monadic plumbing.
newtype FFM f a = FFM { runFFM :: forall r . (a -> r) -> (f r -> r) -> r }
instance Functor (FFM f) where
fmap f (FFM h) = FFM (\r j -> h (r . f) j)
{-# INLINE fmap #-}
instance Applicative (FFM f) where
pure = return
(FFM h) <*> (FFM k) = FFM (\r j -> h (\f -> k (r . f) j) j)
{-# INLINE (<*>) #-}
instance Monad (FFM f) where
return x = FFM (\r _j -> r x)
{-# INLINE return #-}
(FFM h) >>= f = FFM (\r j -> h (\x -> runFFM (f x) r j) j)
{-# INLINE (>>=) #-}
instance Fresh m => Fresh (FFM m) where
fresh = liftFFM . fresh
{-# INLINE fresh #-}
liftFFM :: Monad m => m a -> FFM m a
liftFFM m = FFM (\r j -> j (liftM r m))
{-# INLINE liftFFM #-}
retractFFM :: Monad m => FFM m a -> m a
retractFFM (FFM h) = h return j
where
j mmf = mmf >>= \mf -> mf
{-# INLINE retractFFM #-}
-- | The result of @'nthPatFind' a i@ is @Left k@ where @i-k@ is the
-- number of names in pattern @a@ (with @k < i@) or @Right x@ where @x@
-- is the @i@th name in @a@
newtype NthPatFind = NthPatFind { runNthPatFind :: Integer -> Either Integer AnyName }
instance Monoid NthPatFind where
mempty = NthPatFind Left
mappend (NthPatFind f) (NthPatFind g) =
NthPatFind $ \i -> case f i of
Left i' -> g i'
found@Right {} -> found
-- | The result of @'namePatFind' a x@ is either @Left i@ if @a@ is a pattern that
-- contains @i@ free names none of which are @x@, or @Right j@ if @x@ is the @j@th name
-- in @a@
newtype NamePatFind = NamePatFind { runNamePatFind :: AnyName
-- Left - names skipped over
-- Right - index of the name we found
-> Either Integer Integer }
instance Monoid NamePatFind where
mempty = NamePatFind (\_ -> Left 0)
mappend (NamePatFind f) (NamePatFind g) =
NamePatFind $ \nm -> case f nm of
ans@Right {} -> ans
Left n -> case g nm of
Left m -> Left $! n + m
Right i -> Right $! n + i
-- | The "Generic" representation version of 'Alpha'
class GAlpha f where
gaeq :: AlphaCtx -> f a -> f a -> Bool
gfvAny :: (Contravariant g, Applicative g) => AlphaCtx -> (AnyName -> g AnyName) -> f a -> g (f a)
gclose :: AlphaCtx -> NamePatFind -> f a -> f a
gopen :: AlphaCtx -> NthPatFind -> f a -> f a
gisPat :: f a -> DisjointSet AnyName
gisTerm :: f a -> All
gnthPatFind :: f a -> NthPatFind
gnamePatFind :: f a -> NamePatFind
gswaps :: AlphaCtx -> Perm AnyName -> f a -> f a
gfreshen :: Fresh m => AlphaCtx -> f a -> FFM m (f a, Perm AnyName)
glfreshen :: LFresh m => AlphaCtx -> f a -> (f a -> Perm AnyName -> m b) -> m b
gacompare :: AlphaCtx -> f a -> f a -> Ordering
instance (Alpha c) => GAlpha (K1 i c) where
gaeq ctx (K1 c1) (K1 c2) = aeq' ctx c1 c2
{-# INLINE gaeq #-}
gfvAny ctx nfn = fmap K1 . fvAny' ctx nfn . unK1
{-# INLINE gfvAny #-}
gclose ctx b = K1 . close ctx b . unK1
{-# INLINE gclose #-}
gopen ctx b = K1 . open ctx b . unK1
{-# INLINE gopen #-}
gisPat = isPat . unK1
{-# INLINE gisPat #-}
gisTerm = isTerm . unK1
{-# INLINE gisTerm #-}
gnthPatFind = nthPatFind . unK1
{-# INLINE gnthPatFind #-}
gnamePatFind = namePatFind . unK1
{-# INLINE gnamePatFind #-}
gswaps ctx perm = K1 . swaps' ctx perm . unK1
{-# INLINE gswaps #-}
gfreshen ctx = liftM (first K1) . liftFFM . freshen' ctx . unK1
{-# INLINE gfreshen #-}
glfreshen ctx (K1 c) cont = lfreshen' ctx c (cont . K1)
{-# INLINE glfreshen #-}
gacompare ctx (K1 c1) (K1 c2) = acompare' ctx c1 c2
instance GAlpha f => GAlpha (M1 i c f) where
gaeq ctx (M1 f1) (M1 f2) = gaeq ctx f1 f2
{-# INLINE gaeq #-}
gfvAny ctx nfn = fmap M1 . gfvAny ctx nfn . unM1
{-# INLINE gfvAny #-}
gclose ctx b = M1 . gclose ctx b . unM1
{-# INLINE gclose #-}
gopen ctx b = M1 . gopen ctx b . unM1
{-# INLINE gopen #-}
gisPat = gisPat . unM1
{-# INLINE gisPat #-}
gisTerm = gisTerm . unM1
{-# INLINE gisTerm #-}
gnthPatFind = gnthPatFind . unM1
{-# INLINE gnthPatFind #-}
gnamePatFind = gnamePatFind . unM1
{-# INLINE gnamePatFind #-}
gswaps ctx perm = M1 . gswaps ctx perm . unM1
{-# INLINE gswaps #-}
gfreshen ctx = liftM (first M1) . gfreshen ctx . unM1
{-# INLINE gfreshen #-}
glfreshen ctx (M1 f) cont =
glfreshen ctx f (cont . M1)
{-# INLINE glfreshen #-}
gacompare ctx (M1 f1) (M1 f2) = gacompare ctx f1 f2
instance GAlpha U1 where
gaeq _ctx _ _ = True
{-# INLINE gaeq #-}
gfvAny _ctx _nfn _ = pure U1
gclose _ctx _b _ = U1
gopen _ctx _b _ = U1
gisPat _ = mempty
gisTerm _ = mempty
gnthPatFind _ = mempty
gnamePatFind _ = mempty
gswaps _ctx _perm _ = U1
gfreshen _ctx _ = return (U1, mempty)
{-# INLINE gfreshen #-}
glfreshen _ctx _ cont = cont U1 mempty
gacompare _ctx _ _ = EQ
instance GAlpha V1 where
gaeq _ctx _ _ = False
{-# INLINE gaeq #-}
gfvAny _ctx _nfn = pure
gclose _ctx _b _ = undefined
gopen _ctx _b _ = undefined
gisPat _ = mempty
gisTerm _ = mempty
gnthPatFind _ = mempty
gnamePatFind _ = mempty
gswaps _ctx _perm _ = undefined
gfreshen _ctx _ = return (undefined, mempty)
{-# INLINE gfreshen #-}
glfreshen _ctx _ cont = cont undefined mempty
gacompare _ctx _ _ = error "LocallyNameless.gacompare: undefined for empty data types"
instance (GAlpha f, GAlpha g) => GAlpha (f :*: g) where
gaeq ctx (f1 :*: g1) (f2 :*: g2) =
gaeq ctx f1 f2 && gaeq ctx g1 g2
{-# INLINE gaeq #-}
gfvAny ctx nfn (f :*: g) = (:*:) <$> gfvAny ctx nfn f
<*> gfvAny ctx nfn g
{-# INLINE gfvAny #-}
gclose ctx b (f :*: g) = gclose ctx b f :*: gclose ctx b g
{-# INLINE gclose #-}
gopen ctx b (f :*: g) = gopen ctx b f :*: gopen ctx b g
{-# INLINE gopen #-}
gisPat (f :*: g) = gisPat f <> gisPat g
{-# INLINE gisPat #-}
gisTerm (f :*: g) = gisTerm f <> gisTerm g
{-# INLINE gisTerm #-}
gnthPatFind (f :*: g) = gnthPatFind f <> gnthPatFind g
{-# INLINE gnthPatFind #-}
gnamePatFind (f :*: g) = gnamePatFind f <> gnamePatFind g
{-# INLINE gnamePatFind #-}
gswaps ctx perm (f :*: g) =
gswaps ctx perm f :*: gswaps ctx perm g
{-# INLINE gswaps #-}
gfreshen ctx (f :*: g) = do
~(g', perm2) <- gfreshen ctx g
~(f', perm1) <- gfreshen ctx (gswaps ctx perm2 f)
return (f' :*: g', perm1 <> perm2)
{-# INLINE gfreshen #-}
glfreshen ctx (f :*: g) cont =
glfreshen ctx g $ \g' perm2 ->
glfreshen ctx (gswaps ctx perm2 f) $ \f' perm1 ->
cont (f' :*: g') (perm1 <> perm2)
{-# INLINE glfreshen #-}
gacompare ctx (f1 :*: g1) (f2 :*: g2) =
(gacompare ctx f1 f2) <> (gacompare ctx g1 g2)
instance (GAlpha f, GAlpha g) => GAlpha (f :+: g) where
gaeq ctx (L1 f1) (L1 f2) = gaeq ctx f1 f2
gaeq ctx (R1 g1) (R1 g2) = gaeq ctx g1 g2
gaeq _ctx _ _ = False
{-# INLINE gaeq #-}
gfvAny ctx nfn (L1 f) = fmap L1 (gfvAny ctx nfn f)
gfvAny ctx nfn (R1 g) = fmap R1 (gfvAny ctx nfn g)
{-# INLINE gfvAny #-}
gclose ctx b (L1 f) = L1 (gclose ctx b f)
gclose ctx b (R1 g) = R1 (gclose ctx b g)
{-# INLINE gclose #-}
gopen ctx b (L1 f) = L1 (gopen ctx b f)
gopen ctx b (R1 g) = R1 (gopen ctx b g)
{-# INLINE gopen #-}
gisPat (L1 f) = gisPat f
gisPat (R1 g) = gisPat g
{-# INLINE gisPat #-}
gisTerm (L1 f) = gisTerm f
gisTerm (R1 g) = gisTerm g
{-# INLINE gisTerm #-}
gnthPatFind (L1 f) = gnthPatFind f
gnthPatFind (R1 g) = gnthPatFind g
{-# INLINE gnthPatFind #-}
gnamePatFind (L1 f) = gnamePatFind f
gnamePatFind (R1 g) = gnamePatFind g
{-# INLINE gnamePatFind #-}
gswaps ctx perm (L1 f) = L1 (gswaps ctx perm f)
gswaps ctx perm (R1 f) = R1 (gswaps ctx perm f)
{-# INLINE gswaps #-}
gfreshen ctx (L1 f) = liftM (first L1) (gfreshen ctx f)
gfreshen ctx (R1 f) = liftM (first R1) (gfreshen ctx f)
{-# INLINE gfreshen #-}
glfreshen ctx (L1 f) cont =
glfreshen ctx f (cont . L1)
glfreshen ctx (R1 g) cont =
glfreshen ctx g (cont . R1)
{-# INLINE glfreshen #-}
gacompare _ctx (L1 _) (R1 _) = LT
gacompare _ctx (R1 _) (L1 _) = GT
gacompare ctx (L1 f1) (L1 f2) = gacompare ctx f1 f2
gacompare ctx (R1 g1) (R1 g2) = gacompare ctx g1 g2
{-# INLINE gacompare #-}
-- ============================================================
-- Alpha instances for the usual types
instance Alpha Int where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance Alpha Char where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance Alpha Integer where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance Alpha Float where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance Alpha Double where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance (Integral n, Alpha n) => Alpha (Ratio n) where
aeq' _ctx i j = i == j
fvAny' _ctx _nfn i = pure i
close _ctx _b i = i
open _ctx _b i = i
isPat _ = mempty
isTerm _ = mempty
nthPatFind _ = mempty
namePatFind _ = mempty
swaps' _ctx _p i = i
freshen' _ctx i = return (i, mempty)
lfreshen' _ctx i cont = cont i mempty
acompare' _ctx i j = compare i j
instance Alpha Bool
instance Alpha a => Alpha (Maybe a)
instance Alpha a => Alpha [a]
instance Alpha ()
instance (Alpha a,Alpha b) => Alpha (Either a b)
instance (Alpha a,Alpha b) => Alpha (a,b)
instance (Alpha a,Alpha b,Alpha c) => Alpha (a,b,c)
instance (Alpha a, Alpha b,Alpha c, Alpha d) => Alpha (a,b,c,d)
instance (Alpha a, Alpha b,Alpha c, Alpha d, Alpha e) =>
Alpha (a,b,c,d,e)
-- ============================================================
-- Alpha instances for interesting types
instance Typeable a => Alpha (Name a) where
aeq' ctx n1 n2 =
if isTermCtx ctx
then n1 == n2 -- in terms, better be the same name
else True -- in a pattern, names are always equivlent (since
-- they're both bound, so they can vary).
fvAny' ctx nfn nm = if isTermCtx ctx && isFreeName nm
then contramap AnyName (nfn (AnyName nm))
else pure nm
open ctx b a@(Bn l k) =
if ctxMode ctx == Term && ctxLevel ctx == l
then case runNthPatFind b k of
Right (AnyName nm) -> case gcast nm of
Just nm' -> nm'
Nothing -> error "LocallyNameless.open: inconsistent sorts"
Left _ -> error "LocallyNameless.open : inconsistency - pattern had too few variables"
else
a
open _ctx _ a = a
close ctx b a@(Fn _ _) =
if isTermCtx ctx
then case runNamePatFind b (AnyName a) of
Right k -> Bn (ctxLevel ctx) k
Left _ -> a
else a
close _ctx _ a = a
isPat n = if isFreeName n
then singletonDisjointSet (AnyName n)
else inconsistentDisjointSet
isTerm _ = mempty
nthPatFind nm = NthPatFind $ \i ->
if i == 0 then Right (AnyName nm) else Left $! i-1
namePatFind nm1 = NamePatFind $ \(AnyName nm2) ->
case gcast nm1 of
Just nm1' -> if nm1' == nm2 then Right 0 else Left 1
Nothing -> Left 1
swaps' ctx perm nm =
if isTermCtx ctx
then case apply perm (AnyName nm) of
AnyName nm' ->
case gcast nm' of
Just nm'' -> nm''
Nothing -> error "Internal error swaps' on a Name returned permuted name of wrong sort"
else nm
freshen' ctx nm =
if not (isTermCtx ctx)
then do
nm' <- fresh nm
return (nm', single (AnyName nm) (AnyName nm'))
else error "freshen' on a Name in term position"
lfreshen' ctx nm cont =
if not (isTermCtx ctx)
then do
nm' <- lfresh nm
avoid [AnyName nm'] $ cont nm' $ single (AnyName nm) (AnyName nm')
else error "lfreshen' on a Name in term position"
acompare' ctx (Fn s1 i1) (Fn s2 i2)
| isTermCtx ctx = (compare s1 s2) <> (compare i1 i2)
acompare' ctx n1@(Bn i1 j1) n2@(Bn i2 j2)
| isTermCtx ctx = mconcat [ compare (typeOf n1) (typeOf n2)
, compare i1 i2
, compare j1 j2
]
acompare' ctx (Fn _ _) (Bn _ _) | isTermCtx ctx = LT
acompare' ctx (Bn _ _) (Fn _ _) | isTermCtx ctx = GT
acompare' _ _ _ = EQ
instance Alpha AnyName where
aeq' ctx x y =
if x == y
then True
else
-- in a term unequal variables are unequal, in a pattern it's
-- ok.
not (isTermCtx ctx)
fvAny' ctx nfn n@(AnyName nm) = if isTermCtx ctx && isFreeName nm
then nfn n
else pure n
isTerm _ = mempty
isPat n@(AnyName nm) = if isFreeName nm
then singletonDisjointSet n
else inconsistentDisjointSet
swaps' ctx perm n =
if isTermCtx ctx
then apply perm n
else n
freshen' ctx (AnyName nm) =
if isTermCtx ctx
then error "LocallyNameless.freshen' on AnyName in Term mode"
else do
nm' <- fresh nm
return (AnyName nm', single (AnyName nm) (AnyName nm'))
lfreshen' ctx (AnyName nm) cont =
if isTermCtx ctx
then error "LocallyNameless.lfreshen' on AnyName in Term mode"
else do
nm' <- lfresh nm
avoid [AnyName nm'] $ cont (AnyName nm') $ single (AnyName nm) (AnyName nm')
open ctx b (AnyName nm) = AnyName (open ctx b nm)
close ctx b (AnyName nm) = AnyName (close ctx b nm)
nthPatFind nm = NthPatFind $ \i ->
if i == 0 then Right nm else Left $! i - 1
namePatFind nmHave = NamePatFind $ \nmWant ->
if nmHave == nmWant then Right 0 else Left 1
acompare' _ x y | x == y = EQ
acompare' ctx (AnyName n1) (AnyName n2)
| isTermCtx ctx =
case compare (typeOf n1) (typeOf n2) of
EQ -> case gcast n2 of
Just n2' -> acompare' ctx n1 n2'
Nothing -> error "LocallyNameless.acompare': Equal type representations, but gcast failed in comparing two AnyName values"
ord -> ord
acompare' _ _ _ = EQ