packages feed

rebound-0.1.0.0: src/Rebound/Classes.hs

-- |
-- Module      : Rebound.Classes
-- Description : Type class definitions
--
-- Main typeclasses used by the library.

{-# LANGUAGE DefaultSignatures #-}
module Rebound.Classes where

import Rebound.Lib
import Data.LocalName
import Data.Scoped.List(List, pattern Nil, pattern (:<))
import Data.Scoped.List qualified as List

import Data.Foldable
import Data.Vec qualified as Vec
import Data.Fin qualified as Fin
import Data.Set (Set)
import Data.Set qualified as Set

import GHC.Generics (Generic1(..))

----------------------------------------------------------
-- Indices/variables shifting
----------------------------------------------------------

-- | Bring a scoped type into a new, bigger, scope, through variable shifting.
-- 
-- This class is used for types which are scoped, yet do not allow
-- substitution in general. Typical examples are data-structures which
-- associate metadata to variables.
-- See 'Rebound.Refinement.Refinement' for an example. 
class Shiftable t where
  shift :: SNat k -> t n -> t (k + n)
  -- a good default implementation of this is `shiftFromApply`. But the 
  -- `Subst` class is not yet in scope.  
  
----------------------------------------------------------
-- Free variables
----------------------------------------------------------

-- | Computes the set of free variables in a term.
class FV (t :: Nat -> Type) where
  -- | Does a particular variable appear free?
  appearsFree :: Fin n -> t n -> Bool
  default appearsFree :: (Generic1 t, GFV (Rep1 t)) => Fin n -> t n -> Bool
  appearsFree x e = gappearsFree x (from1 e)
  {-# INLINE appearsFree #-}

  -- | Calculate all of the free variables in a term.
  freeVars :: t n -> Set (Fin n)
  default freeVars :: (Generic1 t, GFV (Rep1 t)) => t n -> Set (Fin n)
  freeVars e = gfreeVars (from1 e)
  {-# INLINE freeVars #-}

-- | Generic programming support for 'FV'.
class GFV (t :: Nat -> Type) where
  gappearsFree :: Fin n -> t n -> Bool
  gfreeVars :: t n -> Set (Fin n)

----------------------------------------------------------
-- * Strengthening
----------------------------------------------------------

-- Strengthening cannot be implemented through substitution because it
-- must fail if the term uses invalid variables. Therefore, we make a
-- class of scoped types that can be strengthened.

-- | Eliminates the most recently bound variable from the term (if unused).
strengthen :: forall n t. (Strengthen t, SNatI n) => t (S n) -> Maybe (t n)
strengthen = strengthenRec s0 s1 (snat :: SNat n)

-- | Eliminates the @n@ most recently bound variables from the term (if unused).
strengthenN :: forall m n t. (Strengthen t, SNatI n) => SNat m -> t (m + n) -> Maybe (t n)
strengthenN m = strengthenRec s0 m (snat :: SNat n)

-- | Bring scoped terms into a smaller scope, if possible.
--
-- Strengthening is only possible if the term only refers to variable which
-- are in the smaller scope.
class Strengthen t where
  -- generalize strengthening -- remove m variables from the middle of the scope
  strengthenRec :: SNat k -> SNat m -> SNat n -> t (k + (m + n)) -> Maybe (t (k + n))
  default strengthenRec :: (Generic1 t, GStrengthen (Rep1 t)) =>
     SNat k -> SNat m -> SNat n -> t (k + (m + n)) -> Maybe (t (k + n))
  strengthenRec k m n t = to1 <$> gstrengthenRec k m n (from1 t)

  -- Remove a single variable from the middle of the scope
  strengthenOneRec :: forall k n. SNat k -> SNat n -> t (k + S n) -> Maybe (t (k + n))
  strengthenOneRec k = strengthenRec k s1

-- | Generic programming support for 'Strengthen'.
class GStrengthen (t :: Nat -> Type) where
  gstrengthenRec :: SNat k -> SNat m -> SNat n -> t (k + (m + n)) -> Maybe (t (k + n))

----------------------------------------------------------
-- FV and Strengthen instances for Data.Scoped.List
---------------------------------------------------------

instance FV t => FV (List t) where
  appearsFree :: Fin n -> List t n -> Bool
  appearsFree x = List.any (appearsFree x)

  freeVars :: List t n -> Set (Fin n)
  freeVars = List.foldr (\x s -> freeVars x `Set.union` s) Set.empty

instance Strengthen t => Strengthen (List t) where
  strengthenRec :: SNat k -> SNat m -> SNat n -> List t (k + (m + n)) -> Maybe (List t (k + n))
  strengthenRec k m n Nil = Just Nil
  strengthenRec k m n (x :< xs) = (:<) <$> strengthenRec k m n x <*> strengthenRec k m n xs

----------------------------------------------------------
-- FV and Strengthen instances for Fin
---------------------------------------------------------

instance FV Fin where
  appearsFree = (==)
  freeVars = Set.singleton

instance Strengthen Fin where
  strengthenRec :: SNat k -> SNat m -> SNat n-> Fin (k + (m + n)) -> Maybe (Fin (k + n))
  strengthenRec = Fin.strengthenRecFin

-- | Update a set of free variables to a new scope through strengthening
rescope :: forall n k. SNat k -> Set (Fin (k + n)) -> Set (Fin n)
rescope k = foldMap g where
   g :: Fin (k + n) -> Set (Fin n)
   g x = maybe
     Set.empty Set.singleton
     (Fin.strengthenRecFin s0 k (undefined :: SNat n) x)

----------------------------------------------------------
-- Type classes for patterns
----------------------------------------------------------

-- | Calculate the number of binding variables in the pattern
-- This number does not need to be an explicit parameter of the type
-- so that we have flexibility about what types we can use as
-- patterns.
class Sized (t :: Type) where
  -- Retrieve size from the type (number of variables bound by the pattern)
  type Size t :: Nat
  -- Access size as a term
  size :: t -> SNat (Size t)

-- | Pairs of types that can be compared with each other as patterns
class PatEq (t1 :: Type) (t2 :: Type) where
    patEq :: t1 -> t2 -> Maybe (Size t1 :~: Size t2)

-- | Class of patterns that are indexed by a natural number
-- where the size is that index directly
class (Sized (t p), Size (t p) ~ p) => SizeIndex t p


---------------------------------------------------------
-- Pattern Class Instances for Prelude and Lib Types
---------------------------------------------------------

-- ** LocalNames

instance Sized LocalName where
  type Size LocalName = N1
  size _ = s1

instance PatEq LocalName LocalName where
  patEq p1 p2 = Just Refl

-- ** SNats
instance Sized (SNat n) where
  type Size (SNat n) = n
  size n = n

instance PatEq (SNat n1) (SNat n2) where
  patEq = testEquality


-- ** Vectors

instance Sized (Vec n a) where
  type Size (Vec n a) = n
  size = Vec.vlength

instance Eq a => PatEq (Vec n1 a) (Vec n2 a) where
  patEq VNil VNil = Just Refl
  patEq (x ::: xs) (y ::: ys) | x == y,
    Just Refl <- patEq xs ys
    = Just Refl
  patEq _ _ = Nothing

-- ** Unit (trivial)

instance Sized () where { type Size () = N0 ;  size _ = SZ }

instance PatEq () () where patEq _ _ = Just Refl

-- ** Pairs

instance (Sized a, Sized b) => Sized (a,b) where
   type Size (a,b) = Size a + Size b
   size (x,y) = sPlus (size x) (size y)

instance (PatEq a1 a2, PatEq b1 b2) => PatEq (a1, b1) (a2, b2) where
   patEq (x1,y1) (x2,y2)
     | Just Refl <- patEq x1 x2
     , Just Refl <- patEq y1 y2
     = Just Refl
   patEq _ _ = Nothing

------------------------------------------