-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathComposition.idr
More file actions
228 lines (200 loc) · 7.71 KB
/
Copy pathComposition.idr
File metadata and controls
228 lines (200 loc) · 7.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
-- SPDX-License-Identifier: MPL-2.0
-- Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
-- Filesystem Composition - Operation Sequences and Undo/Redo
--
-- Proves that sequences of reversible operations are themselves reversible.
-- This is the foundation for the undo/redo mechanism.
module Filesystem.Composition
import Filesystem.Model
import Filesystem.Operations
import Data.List
%default total
--------------------------------------------------------------------------------
-- Operation Type
--------------------------------------------------------------------------------
||| Abstract representation of filesystem operations
public export
data Operation : Type where
||| Create directory
OpMkdir : Path -> Operation
||| Remove directory
OpRmdir : Path -> Operation
||| Create file
OpTouch : Path -> Operation
||| Remove file
OpRm : Path -> Operation
||| Write to file
OpWrite : Path -> FileContent -> Operation
%name Operation op, op1, op2
||| Check if operation is reversible
export
isReversible : Operation -> Bool
isReversible (OpMkdir _) = True
isReversible (OpRmdir _) = True
isReversible (OpTouch _) = True
isReversible (OpRm _) = True
isReversible (OpWrite _ _) = True
||| All operations are reversible in our model
export
allOpsReversible : (op : Operation) -> isReversible op = True
allOpsReversible (OpMkdir _) = Refl
allOpsReversible (OpRmdir _) = Refl
allOpsReversible (OpTouch _) = Refl
allOpsReversible (OpRm _) = Refl
allOpsReversible (OpWrite _ _) = Refl
--------------------------------------------------------------------------------
-- Inverse Operation
--------------------------------------------------------------------------------
||| Compute the inverse of an operation
export
inverse : Operation -> Operation
inverse (OpMkdir p) = OpRmdir p
inverse (OpRmdir p) = OpMkdir p
inverse (OpTouch p) = OpRm p
inverse (OpRm p) = OpTouch p
inverse (OpWrite p content) = OpWrite p content -- Need old content!
||| Inverse of inverse is identity
export
inverseInvolution : (op : Operation) -> inverse (inverse op) = op
inverseInvolution (OpMkdir p) = Refl
inverseInvolution (OpRmdir p) = Refl
inverseInvolution (OpTouch p) = Refl
inverseInvolution (OpRm p) = Refl
inverseInvolution (OpWrite p c) = Refl
--------------------------------------------------------------------------------
-- Operation Application
--------------------------------------------------------------------------------
||| Apply a single operation to filesystem (partial, needs preconditions)
|||
||| In practice, this would check preconditions and return Maybe Filesystem.
||| For now, we assume preconditions are checked externally.
export
partial
applyOp : Operation -> Filesystem -> Filesystem
applyOp (OpMkdir p) fs = mkdir p fs -- Needs precondition proof
applyOp (OpRmdir p) fs = rmdir p fs -- Needs precondition proof
applyOp (OpTouch p) fs = touch p fs -- Needs precondition proof
applyOp (OpRm p) fs = rm p fs -- Needs precondition proof
applyOp (OpWrite p c) fs = writeFile p c fs -- Needs precondition proof
||| Apply a sequence of operations
export
partial
applySequence : List Operation -> Filesystem -> Filesystem
applySequence [] fs = fs
applySequence (op :: ops) fs = applySequence ops (applyOp op fs)
--------------------------------------------------------------------------------
-- Reversibility of Sequences
--------------------------------------------------------------------------------
||| Reversing a sequence and applying inverses undoes the sequence
|||
||| This is the key theorem proving that undo/redo works:
||| If we apply ops to fs, then apply reverse(map(inverse, ops)),
||| we get back to the original fs.
export
partial -- Needs totality proof with proper precondition handling
sequenceReversible :
(ops : List Operation) ->
(fs : Filesystem) ->
(allReversible : All (\op => isReversible op = True) ops) ->
applySequence (reverse (map inverse ops)) (applySequence ops fs) = fs
sequenceReversible [] fs _ = Refl -- Base case: empty sequence
sequenceReversible (op :: ops) fs (prf :: prfs) =
-- Inductive case:
-- applySequence (reverse (map inverse (op :: ops))) (applySequence (op :: ops) fs)
-- = applySequence (reverse (map inverse ops) ++ [inverse op]) (applySequence ops (applyOp op fs))
-- = applySequence [inverse op] (applySequence (reverse (map inverse ops)) (applySequence ops (applyOp op fs)))
-- By IH: applySequence (reverse (map inverse ops)) (applySequence ops (applyOp op fs)) = applyOp op fs
-- = applySequence [inverse op] (applyOp op fs)
-- = applyOp (inverse op) (applyOp op fs)
-- By reversibility of op: = fs
?sequenceReversibleProof
--------------------------------------------------------------------------------
-- Composition Properties
--------------------------------------------------------------------------------
||| Composition of reversible operations is reversible
export
compositionReversible :
(op1, op2 : Operation) ->
(fs : Filesystem) ->
isReversible op1 = True ->
isReversible op2 = True ->
applyOp (inverse op2) (applyOp (inverse op1) (applyOp op1 (applyOp op2 fs))) = fs
compositionReversible op1 op2 fs rev1 rev2 = ?compositionReversibleProof
||| Sequences can be split and reversed independently
export
sequenceSplit :
(ops1, ops2 : List Operation) ->
(fs : Filesystem) ->
applySequence (ops1 ++ ops2) fs =
applySequence ops2 (applySequence ops1 fs)
sequenceSplit [] ops2 fs = Refl
sequenceSplit (op :: ops1) ops2 fs =
rewrite sequenceSplit ops1 ops2 (applyOp op fs) in Refl
||| Reverse of concatenation is concatenation of reverses (reversed)
export
reverseConcat :
(xs, ys : List a) ->
reverse (xs ++ ys) = reverse ys ++ reverse xs
reverseConcat [] ys = sym $ appendNilRightNeutral (reverse ys)
reverseConcat (x :: xs) ys =
rewrite reverseConcat xs ys in
rewrite appendAssociative (reverse ys) (reverse xs) [x] in
Refl
--------------------------------------------------------------------------------
-- Undo/Redo Stack
--------------------------------------------------------------------------------
||| Undo/redo state with operation history
public export
record UndoState where
constructor MkUndoState
||| Current filesystem
current : Filesystem
||| Undo stack (operations that can be undone)
undoStack : List Operation
||| Redo stack (operations that can be redone)
redoStack : List Operation
||| Initial undo state
export
initialUndoState : Filesystem -> UndoState
initialUndoState fs = MkUndoState fs [] []
||| Execute an operation and add to undo stack
export
partial
execute : Operation -> UndoState -> UndoState
execute op (MkUndoState fs undos redos) =
MkUndoState (applyOp op fs) (op :: undos) [] -- Clear redo stack
||| Undo the last operation
export
partial
undo : UndoState -> Maybe UndoState
undo (MkUndoState fs [] redos) = Nothing -- Nothing to undo
undo (MkUndoState fs (op :: undos) redos) =
Just $ MkUndoState (applyOp (inverse op) fs) undos (op :: redos)
||| Redo the last undone operation
export
partial
redo : UndoState -> Maybe UndoState
redo (MkUndoState fs undos []) = Nothing -- Nothing to redo
redo (MkUndoState fs undos (op :: redos)) =
Just $ MkUndoState (applyOp op fs) (op :: undos) redos
||| Undo followed by redo is identity
export
partial
undoRedoIdentity :
(state : UndoState) ->
(op : Operation) ->
redo (fromJust $ undo (execute op state)) = Just (execute op state)
undoRedoIdentity state op = ?undoRedoIdentityProof
||| Multiple undos can be redone
export
partial
undoRedoComposition :
(state : UndoState) ->
(n : Nat) ->
(canUndo : length (undoStack state) >= n) ->
applyN n redo (applyN n (fromJust . undo) state) = Just state
undoRedoComposition state n canUndo = ?undoRedoCompositionProof
where
applyN : Nat -> (a -> Maybe a) -> a -> Maybe a
applyN Z f x = Just x
applyN (S k) f x = f x >>= applyN k f