packages feed

compaREST-0.1.0.0: src/Data/OpenApi/Compare/Paths.hs

-- | Utilities for traversing heterogeneous trees. A heterogeneous tree is a
-- collection of datatypes that "contain" eachother in some form of tree
-- structure.
module Data.OpenApi.Compare.Paths
  ( NiceQuiver,
    AdditionalQuiverConstraints,
    Paths (..),
    DiffPaths (..),
    catDiffPaths,
    AnItem (..),
    step,

    -- * Reexports
    (>>>),
    (<<<),
  )
where

import Control.Category
import Data.Kind
import Data.Type.Equality
import Type.Reflection
import Prelude hiding ((.))

type NiceQuiver (q :: k -> j -> Type) (a :: k) (b :: j) =
  (Typeable q, Typeable a, Typeable b, Ord (q a b), Show (q a b), AdditionalQuiverConstraints q a b)

type family AdditionalQuiverConstraints (q :: k -> j -> Type) (a :: k) (b :: j) :: Constraint

-- | All the possible ways to navigate between nodes in a heterogeneous tree
-- form a quiver. The hom-sets of the free category constructed from this quiver
-- are the sets of various multi-step paths between nodes. This is similar to a
-- list, but indexed. The list is in reverse because in practice we append
-- items at the end one at a time.
data Paths (q :: k -> k -> Type) (a :: k) (b :: k) where
  Root :: Paths q a a
  Snoc :: NiceQuiver q b c => Paths q a b -> !(q b c) -> Paths q a c

infixl 5 `Snoc`

deriving stock instance Show (Paths q a b)

step :: NiceQuiver q a b => q a b -> Paths q a b
step s = Root `Snoc` s

instance Category (Paths q) where
  id = Root
  Root . xs = xs
  (Snoc ys y) . xs = Snoc (ys . xs) y

typeRepRHS :: Typeable b => Paths q a b -> TypeRep b
typeRepRHS _ = typeRep

typeRepLHS :: Typeable b => Paths q a b -> TypeRep a
typeRepLHS Root = typeRep
typeRepLHS (Snoc xs _) = typeRepLHS xs

instance TestEquality (Paths q a) where
  testEquality Root Root = Just Refl
  testEquality Root (Snoc ys _) = testEquality (typeRepLHS ys) typeRep
  testEquality (Snoc xs _) Root = testEquality typeRep (typeRepLHS xs)
  testEquality (Snoc _ _) (Snoc _ _) = testEquality typeRep typeRep

instance Eq (Paths q a b) where
  Root == Root = True
  Snoc xs x == Snoc ys y
    | Just Refl <- testEquality (typeRepRHS xs) (typeRepRHS ys) =
      xs == ys && x == y
  _ == _ = False

instance Ord (Paths q a b) where
  compare Root Root = EQ
  compare Root (Snoc _ _) = LT
  compare (Snoc _ _) Root = GT
  compare (Snoc xs x) (Snoc ys y) =
    case testEquality (typeRepRHS xs) (typeRepRHS ys) of
      Just Refl -> compare xs ys <> compare x y
      Nothing -> compare (someTypeRep xs) (someTypeRep ys)

-- | Like a differece list, but indexed.
newtype DiffPaths (q :: k -> k -> Type) (a :: k) (b :: k)
  = DiffPaths (forall c. Paths q c a -> Paths q c b)

catDiffPaths :: DiffPaths q a b -> DiffPaths q b c -> DiffPaths q a c
catDiffPaths (DiffPaths f) (DiffPaths g) = DiffPaths (g . f)

-- _DiffPaths :: Iso (DiffPaths q a b) (DiffPaths q c d) (Paths q a b) (Paths q c d)
-- _DiffPaths = dimap (\(DiffPaths f) -> f Root) $
--   fmap $ \xs -> DiffPaths (>>> xs)

-- | An item related to some path relative to the root @r@.
data AnItem (q :: k -> k -> Type) (f :: k -> Type) (r :: k) where
  AnItem :: Ord (f a) => Paths q r a -> !(f a) -> AnItem q f r

-- the Ord is yuck but we need it and it should be fine in monomorphic cases

instance Eq (AnItem q f r) where
  AnItem xs fx == AnItem ys fy
    | Just Refl <- testEquality xs ys =
      xs == ys && fx == fy
  _ == _ = False

instance Typeable r => Ord (AnItem q f r) where
  compare (AnItem xs fx) (AnItem ys fy) =
    case testEquality xs ys of
      Just Refl -> compare xs ys <> compare fx fy
      Nothing -> case xs of
        Root -> case ys of
          Root -> compare (someTypeRep xs) (someTypeRep ys)
          Snoc _ _ -> compare (someTypeRep xs) (someTypeRep ys)
        Snoc _ _ -> case ys of
          Root -> compare (someTypeRep xs) (someTypeRep ys)
          Snoc _ _ -> compare (someTypeRep xs) (someTypeRep ys)