1{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE DerivingStrategies #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE PolyKinds #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
9-- | The interface for types which represent changes made to other types
10module Data.Patch.Class where
12import qualified Data.Semigroupoid as Cat
13import Data.Functor.Identity
14import Data.Functor.Misc
16import Data.Semigroup (Semigroup(..))
18import Data.Type.Equality ((:~:) (..))
21 type PatchSource p :: *
22 type PatchTarget p :: *
23 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
28 -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
33 -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
34 applyHet p a = case apply p a of
38-- | Apply a 'PatchHet'; if it does nothing, return the original value
39applyAlwaysHet :: PatchHet p => p -> PatchSource p -> PatchTarget p
40applyAlwaysHet p t = case applyHet p t of
44-- | A 'Patch' type represents a kind of change made to a datastructure.
46-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
47-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
49 , PatchSource p ~ PatchTarget p
51 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
53 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
55-- | Apply a 'Patch'; if it does nothing, return the original value
56applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
57applyAlways p t = fromMaybe t $ apply p t
59-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
60instance PatchHet (Identity a) where
61 type PatchSource (Identity a) = a
62 type PatchTarget (Identity a) = a
63instance Patch (Identity a) where
64 apply (Identity a) _ = Just a
66-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
67instance PatchHet (Proxy (a :: *)) where
68 type PatchSource (Proxy a) = a
69 type PatchTarget (Proxy a) = a
70instance Patch (Proxy (a :: *)) where
71 apply ~Proxy _ = Nothing
73-- | Like '(.)', but composes functions that return patches rather than
74-- functions that return new values. The Semigroup instance for patches must
75-- apply patches right-to-left, like '(.)'.
77 :: (Patch p, Semigroup p)
78 => (PatchTarget p -> p)
79 -> (PatchTarget p -> p)
81composePatchFunctions g f a =
83 in g (applyAlways fp a) <> fp
86class PatchHet2Base (p :: k -> k -> *) where
87 type PatchSource1 p :: k -> *
88 type PatchTarget1 p :: k -> *
90class ( PatchHet2Base p
91 , PatchHet (p from to)
92 , PatchSource1 p from ~ PatchSource (p from to)
93 , PatchTarget1 p to ~ PatchTarget (p from to)
94 ) => PatchHet2Locally (p :: k -> k -> *) from to where
95instance ( PatchHet2Base p
96 , PatchHet (p from to)
97 , PatchSource1 p from ~ PatchSource (p from to)
98 , PatchTarget1 p to ~ PatchTarget (p from to)
99 ) => PatchHet2Locally (p :: k -> k -> *) from to where
102 :: PatchHet2Locally p from to
104 -> PatchSource1 p from
105 -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
106applyHet2Locally = applyHet
108applyAlwaysHet2Locally
109 :: PatchHet2Locally p from to
111 -> PatchSource1 p from
113applyAlwaysHet2Locally = applyAlwaysHet
115-- TODO once we can use quantified constraints, perhaps combine PatchHet2Base and
116-- PatchHet2Locally, or at least get rid of this.
117class PatchHet2Base p => PatchHet2 (p :: k -> k -> *) where
120 -> PatchSource1 p from
121 -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
126 -> PatchSource1 p from
128applyAlwaysHet2 p t = case applyHet2 p t of
132-- | Connect the classes without quanitified constraints
133newtype ProjectLocal p from to = ProjectLocal { unProjectLocal :: p from to }
134 deriving newtype Cat.Semigroupoid
136instance PatchHet2 p => PatchHet (ProjectLocal p from to) where
137 type PatchSource (ProjectLocal p from to) = PatchSource1 p from
138 type PatchTarget (ProjectLocal p from to) = PatchTarget1 p to
139 applyHet (ProjectLocal p) = applyHet2 p
141instance PatchHet2 p => PatchHet2Base (ProjectLocal p) where
142 type PatchSource1 (ProjectLocal p) = PatchSource1 p
143 type PatchTarget1 (ProjectLocal p) = PatchTarget1 p
145class ( PatchHet2Base p
146 , PatchSource1 p ~ PatchTarget1 p
148instance ( PatchHet2Base p
149 , PatchSource1 p ~ PatchTarget1 p
152-- | 'First2' can be used as a 'Patch' that always fully replaces the value
153instance PatchHet (First2 (t :: k -> *) (from :: k) (to :: k)) where
154 type PatchSource (First2 t from to) = t from
155 type PatchTarget (First2 t from to) = t to
156 applyHet (First2 val) _ = Right val
158-- | 'Proxy3' can be used as a 'Patch' that always does nothing
159instance PatchHet (Proxy3 (t :: k -> *) (a :: k) (a :: k)) where
160 type PatchSource (Proxy3 t a a) = t a
161 type PatchTarget (Proxy3 t a a) = t a
162 applyHet ~Proxy3 _ = Left Refl
164instance PatchHet2Base (Proxy3 (t :: k -> *) :: k -> k -> *) where
165 type PatchSource1 (Proxy3 t) = t
166 type PatchTarget1 (Proxy3 t) = t
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list