|
1 | | -{-# LANGUAGE ExistentialQuantification #-} |
2 | | -{-# LANGUAGE TypeApplications #-} |
3 | | -{-# LANGUAGE ConstraintKinds #-} |
4 | | -{-# LANGUAGE InstanceSigs #-} |
5 | | - |
6 | | -module Drasil.Database.Chunk |
7 | | - ( Chunk, |
8 | | - IsChunk, |
9 | | - HasChunkRefs (..), |
10 | | - mkChunk, -- FIXME: mkChunk should not be exported but is temporarily because this module is NOT in `drasil-database` |
11 | | - unChunk, |
12 | | - chunkType, |
13 | | - ) |
14 | | -where |
| 1 | +{-# LANGUAGE ExistentialQuantification, |
| 2 | + ConstraintKinds, |
| 3 | + InstanceSigs, |
| 4 | + TypeOperators, |
| 5 | + TypeApplications, |
| 6 | + FlexibleContexts, |
| 7 | + UndecidableInstances, |
| 8 | + FlexibleInstances #-} |
| 9 | +module Drasil.Database.Chunk ( |
| 10 | + Chunk, |
| 11 | + IsChunk, |
| 12 | + HasChunkRefs(..), |
| 13 | + mkChunk, -- FIXME: mkChunk should not be exported but is temporarily because this module is NOT in `drasil-database` |
| 14 | + unChunk, |
| 15 | + chunkType |
| 16 | +) where |
15 | 17 |
|
16 | 18 | import Control.Lens ((^.), to, Getter) |
17 | | -import Data.Typeable (Proxy (Proxy), TypeRep, Typeable, cast, typeOf, typeRep) |
18 | 19 | import qualified Data.Set as S |
| 20 | +import Data.Typeable (Proxy (Proxy), TypeRep, Typeable, cast, typeOf, typeRep) |
| 21 | +import GHC.Generics (Generic (Rep, from), M1 (..), K1 (..), type (:*:) (..), |
| 22 | + type (:+:) (..), U1, Generically(..)) |
19 | 23 |
|
20 | 24 | import Drasil.Database.UID (HasUID (..), UID) |
21 | 25 |
|
22 | | --- | All chunks should expose what chunks they reference/rely on, so that we can |
23 | | --- test 'ChunkDB's to ensure all presupposed chunks are already registered. |
24 | | -class HasChunkRefs a where |
25 | | - chunkRefs :: a -> S.Set UID |
26 | | - |
27 | 26 | -- | Constraint for anything that may be considered a valid chunk type. |
28 | 27 | type IsChunk a = (HasUID a, HasChunkRefs a, Typeable a) |
29 | 28 |
|
@@ -56,3 +55,91 @@ unChunk (Chunk c) = cast c |
56 | 55 | -- | Ask a 'Chunk' for the type of data it codifies. |
57 | 56 | chunkType :: Chunk -> TypeRep |
58 | 57 | chunkType (Chunk c) = typeOf c |
| 58 | + |
| 59 | +-- | The majority of chunks will relate other chunks in some way. In other |
| 60 | +-- words, the majority of our chunks *depend* on others. 'HasChunkRefs' is meant |
| 61 | +-- as a way to capture what things a chunk *directly* refers to (i.e., depends |
| 62 | +-- on directly). |
| 63 | +class HasChunkRefs a where |
| 64 | + chunkRefs :: a -> S.Set UID |
| 65 | + |
| 66 | +instance HasChunkRefs UID where |
| 67 | + -- | 'UID's are meant to be "owned" (i.e., they are the unique identifier of |
| 68 | + -- the chunk being defined), not *carried as references to other chunks*. |
| 69 | + -- 'TypedUIDRef t' exists to be used as a *reference to another chunk of type |
| 70 | + -- 't'*. Therefore, `UID` has no chunk references. |
| 71 | + chunkRefs _ = S.empty |
| 72 | + {-# INLINABLE chunkRefs #-} |
| 73 | + |
| 74 | +instance HasChunkRefs Int where |
| 75 | + chunkRefs _ = S.empty |
| 76 | + {-# INLINABLE chunkRefs #-} |
| 77 | + |
| 78 | +instance HasChunkRefs Integer where |
| 79 | + chunkRefs _ = S.empty |
| 80 | + {-# INLINABLE chunkRefs #-} |
| 81 | + |
| 82 | +instance HasChunkRefs Double where |
| 83 | + chunkRefs _ = S.empty |
| 84 | + {-# INLINABLE chunkRefs #-} |
| 85 | + |
| 86 | +instance HasChunkRefs Bool where |
| 87 | + chunkRefs _ = S.empty |
| 88 | + {-# INLINABLE chunkRefs #-} |
| 89 | + |
| 90 | +instance HasChunkRefs Char where |
| 91 | + chunkRefs _ = S.empty |
| 92 | + {-# INLINABLE chunkRefs #-} |
| 93 | + |
| 94 | +-- NOTE: 'OVERLAPPING' instance here because [Char] is instantiated with |
| 95 | +-- `HasChunkRefs [a]`, but very inefficient. We already know the result will be |
| 96 | +-- empty. |
| 97 | +instance {-# OVERLAPPING #-} HasChunkRefs String where |
| 98 | + chunkRefs _ = S.empty |
| 99 | + {-# INLINABLE chunkRefs #-} |
| 100 | + |
| 101 | +instance HasChunkRefs a => HasChunkRefs [a] where |
| 102 | + chunkRefs = S.unions . map chunkRefs |
| 103 | + {-# INLINABLE chunkRefs #-} |
| 104 | + |
| 105 | +instance HasChunkRefs a => HasChunkRefs (Maybe a) where |
| 106 | + chunkRefs Nothing = S.empty |
| 107 | + chunkRefs (Just v) = chunkRefs v |
| 108 | + {-# INLINABLE chunkRefs #-} |
| 109 | + |
| 110 | +instance (HasChunkRefs l, HasChunkRefs r) => HasChunkRefs (Either l r) where |
| 111 | + chunkRefs = either chunkRefs chunkRefs |
| 112 | + {-# INLINABLE chunkRefs #-} |
| 113 | + |
| 114 | +instance (Generic a, GHasCRefs (Rep a)) => HasChunkRefs (Generically a) where |
| 115 | + chunkRefs (Generically a) = gChunkRefs $ from a |
| 116 | + {-# INLINABLE chunkRefs #-} |
| 117 | + |
| 118 | +class GHasCRefs f where |
| 119 | + gChunkRefs :: f p -> S.Set UID |
| 120 | + |
| 121 | +-- Meta-information (constructors, selectors): pass through |
| 122 | +instance GHasCRefs f => GHasCRefs (M1 i c f) where |
| 123 | + gChunkRefs (M1 x) = gChunkRefs x |
| 124 | + {-# INLINABLE gChunkRefs #-} |
| 125 | + |
| 126 | +-- Products: Union |
| 127 | +instance (GHasCRefs a, GHasCRefs b) => GHasCRefs (a :*: b) where |
| 128 | + gChunkRefs (a :*: b) = gChunkRefs a `S.union` gChunkRefs b |
| 129 | + {-# INLINABLE gChunkRefs #-} |
| 130 | + |
| 131 | +-- Sums: Depends on variant |
| 132 | +instance (GHasCRefs a, GHasCRefs b) => GHasCRefs (a :+: b) where |
| 133 | + gChunkRefs (L1 x) = gChunkRefs x |
| 134 | + gChunkRefs (R1 x) = gChunkRefs x |
| 135 | + {-# INLINABLE gChunkRefs #-} |
| 136 | + |
| 137 | +-- Fields: Delegate |
| 138 | +instance HasChunkRefs c => GHasCRefs (K1 i c) where |
| 139 | + gChunkRefs (K1 x) = chunkRefs x |
| 140 | + {-# INLINABLE gChunkRefs #-} |
| 141 | + |
| 142 | +-- Unit: Nothing! |
| 143 | +instance GHasCRefs U1 where |
| 144 | + gChunkRefs _ = S.empty |
| 145 | + {-# INLINABLE gChunkRefs #-} |
0 commit comments