Browse code
chore: split into files
Ed L authored on 06/10/2020 02:44:35
Showing 4 changed files
Showing 4 changed files
... | ... |
@@ -1,245 +1,5 @@ |
1 |
-(defpackage :data-lens.lenses |
|
2 |
- (:shadow :set) |
|
3 |
- (:use :cl) |
|
4 |
- (:export :over :set :view :make-alist-lens :make-plist-lens :make-hash-table-lens |
|
5 |
- :make-list-lens)) |
|
6 |
-(in-package :data-lens.lenses) |
|
7 |
- |
|
8 |
-#+fw.dev |
|
9 |
-(progn |
|
10 |
- ;; maybe functor implementation |
|
11 |
- (defclass maybe () |
|
12 |
- ()) |
|
13 |
- (defclass just (maybe) |
|
14 |
- ((%v :initarg :value :reader value))) |
|
15 |
- (defclass nothing (maybe) |
|
16 |
- ()) |
|
17 |
- |
|
18 |
- (defun just (value) |
|
19 |
- (make-instance 'just :value value)) |
|
20 |
- (defun nothing (&optional value) |
|
21 |
- (declare (ignore value)) |
|
22 |
- (make-instance 'nothing)) |
|
23 |
- |
|
24 |
- (defgeneric maybe (default value) |
|
25 |
- (:method (default (value just)) |
|
26 |
- (value value)) |
|
27 |
- (:method (default (value nothing)) |
|
28 |
- default)) |
|
29 |
- |
|
30 |
- (defgeneric maybe-apply (function value) |
|
31 |
- (:method (function (value just)) |
|
32 |
- (just (funcall function (value value)))) |
|
33 |
- (:method (function (value nothing)) |
|
34 |
- value)) |
|
35 |
- |
|
36 |
- (defmethod print-object ((o just) s) |
|
37 |
- (format s "#.(~s ~s)" |
|
38 |
- 'just |
|
39 |
- (value o))) |
|
40 |
- |
|
41 |
- (defmethod print-object ((o nothing) s) |
|
42 |
- (format s "#.(~s)" |
|
43 |
- 'nothing))) |
|
44 |
- |
|
45 |
-;; identity functor, necessary for set and over |
|
46 |
-(defclass identity- () |
|
47 |
- ((%v :initarg :value :reader unidentity))) |
|
48 |
- |
|
49 |
-(defun wrap-identity (v) |
|
50 |
- (make-instance 'identity- :value v)) |
|
51 |
- |
|
52 |
-(defmethod print-object ((o identity-) s) |
|
53 |
- (format s "#.(~s ~s)" |
|
54 |
- 'wrap-identity |
|
55 |
- (unidentity o))) |
|
56 |
- |
|
57 |
-;; constant functor, necessary for view |
|
58 |
-(defclass constant- () |
|
59 |
- ((%v :initarg :value :reader unconstant))) |
|
60 |
- |
|
61 |
-(defun wrap-constant (v) |
|
62 |
- (make-instance 'constant- :value v)) |
|
63 |
- |
|
64 |
-(defmethod print-object ((o constant-) s) |
|
65 |
- (format s "#.(~s ~s)" |
|
66 |
- 'wrap-constant |
|
67 |
- (unconstant o))) |
|
68 |
- |
|
69 |
-(defgeneric fmap (function data) |
|
70 |
- (:method (function (data identity-)) |
|
71 |
- (wrap-identity |
|
72 |
- (funcall function |
|
73 |
- (unidentity data)))) |
|
74 |
- (:method (function (data constant-)) |
|
75 |
- data) |
|
76 |
- (:method (function (data list)) |
|
77 |
- (mapcar function data)) |
|
78 |
- (:method (function (data vector)) |
|
79 |
- (map 'vector function data)) |
|
80 |
- #+fw.dev |
|
81 |
- (:method (function (data maybe)) |
|
82 |
- (maybe-apply function data))) |
|
83 |
- |
|
84 |
-(defun over (lens cb rec) |
|
85 |
- "Given a lens, a callback and a record, apply the lens to the |
|
86 |
-record, transform it by the callback and return copy of the record, |
|
87 |
-updated to contain the result of the callback. This is the fundamental |
|
88 |
-operation on a lens and SET and VIEW are implemented in terms of it. |
|
89 |
- |
|
90 |
-A lens is any function of the form (lambda (fun) (lambda (rec) ...)) |
|
91 |
-that obeys the lens laws (where == is some reasonable equality |
|
92 |
-operator): |
|
93 |
- |
|
94 |
- (== (view lens (set lens value rec)) |
|
95 |
- value) |
|
96 |
- |
|
97 |
- (== (set lens (view lens rec) rec) |
|
98 |
- rec) |
|
99 |
- |
|
100 |
- (== (set lens value2 (set lens value1 rec)) |
|
101 |
- (set lens value2 rec)) |
|
102 |
- |
|
103 |
-The inner lambda returns a functor that determines the policy to be |
|
104 |
-applied to the focused part. By default, this only uses IDENTITY- and |
|
105 |
-CONSTANT- in order to implement the lens operations over, set and |
|
106 |
-view. |
|
107 |
- |
|
108 |
-If these conditions are met, (over (data-lens:<>1 lens1 lens2) ...) is |
|
109 |
-equivalent to using lens2 to focus the part lens1 focuses: note that |
|
110 |
-composition is \"backwards\" from what one might expect: this is |
|
111 |
-because composition composes the wrapper lambdas and applies the |
|
112 |
-lambda that actually pulls a value out of a record later." |
|
113 |
- (unidentity |
|
114 |
- (funcall (funcall lens (lambda (x) (wrap-identity (funcall cb x)))) |
|
115 |
- rec))) |
|
116 |
- |
|
117 |
-(defun view (lens rec) |
|
118 |
- "Given a lens and a rec, return the focused value" |
|
119 |
- (unconstant |
|
120 |
- (funcall (funcall lens (lambda (x) (wrap-constant x))) |
|
121 |
- rec))) |
|
122 |
- |
|
123 |
-(defun set (lens v rec) |
|
124 |
- "Given a lens, a value and a rec, immutably update the rec to |
|
125 |
-contain the new value at the location focused by the lens." |
|
126 |
- (unidentity |
|
127 |
- (funcall (funcall lens (lambda (_) _ (wrap-identity v))) |
|
128 |
- rec))) |
|
129 |
- |
|
130 |
-#+fw.dev |
|
131 |
-(progn |
|
132 |
- ;; "fake" functors that don't assume a functor result to their |
|
133 |
- ;; callback |
|
134 |
- (defun over* (lens cb rec) |
|
135 |
- (funcall (funcall lens cb) |
|
136 |
- rec)) |
|
137 |
- |
|
138 |
- (defun set* (lens value rec) |
|
139 |
- (over lens |
|
140 |
- (lambda (_) |
|
141 |
- (declare (ignore _)) |
|
142 |
- value) |
|
143 |
- rec)) |
|
144 |
- |
|
145 |
- (defun view* (lens rec) |
|
146 |
- (over lens |
|
147 |
- (lambda (value) |
|
148 |
- (return-from view* |
|
149 |
- value)) |
|
150 |
- rec))) |
|
151 |
- |
|
152 |
-(defun make-alist-history-lens (key) |
|
153 |
- "A lens for updating a alist, preserving previous values" |
|
154 |
- (lambda (cb) |
|
155 |
- (lambda (alist) |
|
156 |
- (fmap (lambda (new) |
|
157 |
- (cons (cons key new) |
|
158 |
- alist)) |
|
159 |
- (funcall cb (serapeum:assocdr key alist)))))) |
|
160 |
- |
|
161 |
-(defun make-alist-lens (key) |
|
162 |
- "A lens for updating a alist, discarding previous values" |
|
163 |
- (lambda (cb) |
|
164 |
- (lambda (alist) |
|
165 |
- (fmap (lambda (new) |
|
166 |
- (remove-duplicates (cons (cons key new) |
|
167 |
- alist) |
|
168 |
- :key #'car |
|
169 |
- :from-end t)) |
|
170 |
- (funcall cb (serapeum:assocdr key alist)))))) |
|
171 |
- |
|
172 |
-(defun make-list-lens (index) |
|
173 |
- "A lens for updating a sequence" |
|
174 |
- (lambda (cb) |
|
175 |
- (lambda (seq) |
|
176 |
- (fmap (lambda (new) |
|
177 |
- (let ((result (copy-seq seq))) |
|
178 |
- (prog1 result |
|
179 |
- (setf (elt result index) new)))) |
|
180 |
- (funcall cb (elt seq index)))))) |
|
181 |
- |
|
182 |
-(defun make-plist-lens (key) |
|
183 |
- "A lens for updating a plist, preserving previous values" |
|
184 |
- (lambda (cb) |
|
185 |
- (lambda (plist) |
|
186 |
- (fmap (lambda (new) |
|
187 |
- (list* key new |
|
188 |
- plist)) |
|
189 |
- (funcall cb (getf plist key)))))) |
|
190 |
- |
|
191 |
-(defun make-hash-table-lens (key) |
|
192 |
- "A lens for updating a hash-table, discarding previous values" |
|
193 |
- (lambda (cb) |
|
194 |
- (lambda (old-hash) |
|
195 |
- (fmap (lambda (new) |
|
196 |
- (let ((new-hash (alexandria:copy-hash-table old-hash))) |
|
197 |
- (prog1 new-hash |
|
198 |
- (setf (gethash key new-hash) |
|
199 |
- new)))) |
|
200 |
- (funcall cb (gethash key old-hash)))))) |
|
201 |
- |
|
202 |
-;; imagine a lens here that uses the MOP to immutably update a class... |
|
203 |
-(defgeneric clone (obj &rest new-initargs &key) |
|
204 |
- (:method :around (obj &rest new-initargs &key) |
|
205 |
- (apply #'reinitialize-instance (call-next-method) new-initargs))) |
|
206 |
- |
|
207 |
-#+fw.demo |
|
208 |
-(progn |
|
209 |
- (defclass foo () |
|
210 |
- ((a :initarg :a :accessor a))) |
|
211 |
- (defmethod clone ((obj foo) &key) |
|
212 |
- (make-instance 'foo :a (a obj))) |
|
213 |
- |
|
214 |
- ;;; needs to be updated for functor-based lens |
|
215 |
- (defun a-lens (cb) |
|
216 |
- (lambda (foo) |
|
217 |
- (fw.lu:prog1-bind (new (clone foo)) |
|
218 |
- (setf (a new) |
|
219 |
- (funcall cb (a foo)))))) |
|
220 |
- (view 'a-lens |
|
221 |
- (over 'a-lens '1+ |
|
222 |
- (set 'a-lens 2 |
|
223 |
- (make-instance 'foo :a 1)))) #| |
|
224 |
- ==> 3 |#) |
|
225 |
- |
|
226 |
- |
|
227 |
- |
|
228 |
-(defpackage :data-lens |
|
229 |
- (:use :cl) |
|
230 |
- (:import-from #:serapeum #:op #:defalias) |
|
231 |
- (:export #:regex-match #:include #:exclude #:pick #:key-transform |
|
232 |
- #:combine #:derive #:cumsum #:over #:on #:shortcut #:defun-ct #:key |
|
233 |
- #:extract-key #:element #:let-fn #:juxt #:transform-tail #:slice |
|
234 |
- #:compress-runs #:combine-matching-lists #:sorted #:applicable-when |
|
235 |
- #:of-length #:of-min-length #:of-max-length #:transform-head |
|
236 |
- #:maximizing #:zipping #:applying #:splice-elt #:transform-elt #:denest |
|
237 |
- #:op #:defalias #:<> #:<>1 #:== #:• |
|
238 |
- )) |
|
239 |
- |
|
240 | 1 |
(in-package :data-lens) |
241 | 2 |
|
242 |
- |
|
243 | 3 |
(declaim |
244 | 4 |
(inline data-lens:over data-lens:transform-tail |
245 | 5 |
data-lens:applicable-when data-lens:of-min-length |
246 | 6 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,219 @@ |
1 |
+(in-package :data-lens.lenses) |
|
2 |
+ |
|
3 |
+;; identity functor, necessary for set and over |
|
4 |
+(defclass identity- () |
|
5 |
+ ((%v :initarg :value :reader unidentity))) |
|
6 |
+ |
|
7 |
+(defun wrap-identity (v) |
|
8 |
+ (make-instance 'identity- :value v)) |
|
9 |
+ |
|
10 |
+(defmethod print-object ((o identity-) s) |
|
11 |
+ (format s "#.(~s ~s)" |
|
12 |
+ 'wrap-identity |
|
13 |
+ (unidentity o))) |
|
14 |
+ |
|
15 |
+;; constant functor, necessary for view |
|
16 |
+(defclass constant- () |
|
17 |
+ ((%v :initarg :value :reader unconstant))) |
|
18 |
+ |
|
19 |
+(defun wrap-constant (v) |
|
20 |
+ (make-instance 'constant- :value v)) |
|
21 |
+ |
|
22 |
+(defmethod print-object ((o constant-) s) |
|
23 |
+ (format s "#.(~s ~s)" |
|
24 |
+ 'wrap-constant |
|
25 |
+ (unconstant o))) |
|
26 |
+ |
|
27 |
+(defgeneric fmap (function data) |
|
28 |
+ (:method (function (data identity-)) |
|
29 |
+ (wrap-identity |
|
30 |
+ (funcall function |
|
31 |
+ (unidentity data)))) |
|
32 |
+ (:method (function (data constant-)) |
|
33 |
+ data) |
|
34 |
+ (:method (function (data list)) |
|
35 |
+ (mapcar function data)) |
|
36 |
+ (:method (function (data vector)) |
|
37 |
+ (map 'vector function data)) |
|
38 |
+ #+fw.dev |
|
39 |
+ (:method (function (data maybe)) |
|
40 |
+ (maybe-apply function data))) |
|
41 |
+ |
|
42 |
+(defun over (lens cb rec) |
|
43 |
+ "Given a lens, a callback and a record, apply the lens to the |
|
44 |
+record, transform it by the callback and return copy of the record, |
|
45 |
+updated to contain the result of the callback. This is the fundamental |
|
46 |
+operation on a lens and SET and VIEW are implemented in terms of it. |
|
47 |
+ |
|
48 |
+A lens is any function of the form (lambda (fun) (lambda (rec) ...)) |
|
49 |
+that obeys the lens laws (where == is some reasonable equality |
|
50 |
+operator): |
|
51 |
+ |
|
52 |
+ (== (view lens (set lens value rec)) |
|
53 |
+ value) |
|
54 |
+ |
|
55 |
+ (== (set lens (view lens rec) rec) |
|
56 |
+ rec) |
|
57 |
+ |
|
58 |
+ (== (set lens value2 (set lens value1 rec)) |
|
59 |
+ (set lens value2 rec)) |
|
60 |
+ |
|
61 |
+The inner lambda returns a functor that determines the policy to be |
|
62 |
+applied to the focused part. By default, this only uses IDENTITY- and |
|
63 |
+CONSTANT- in order to implement the lens operations over, set and |
|
64 |
+view. |
|
65 |
+ |
|
66 |
+If these conditions are met, (over (data-lens:<>1 lens1 lens2) ...) is |
|
67 |
+equivalent to using lens2 to focus the part lens1 focuses: note that |
|
68 |
+composition is \"backwards\" from what one might expect: this is |
|
69 |
+because composition composes the wrapper lambdas and applies the |
|
70 |
+lambda that actually pulls a value out of a record later." |
|
71 |
+ (unidentity |
|
72 |
+ (funcall (funcall lens (lambda (x) (wrap-identity (funcall cb x)))) |
|
73 |
+ rec))) |
|
74 |
+ |
|
75 |
+(defun view (lens rec) |
|
76 |
+ "Given a lens and a rec, return the focused value" |
|
77 |
+ (unconstant |
|
78 |
+ (funcall (funcall lens (lambda (x) (wrap-constant x))) |
|
79 |
+ rec))) |
|
80 |
+ |
|
81 |
+(defun set (lens v rec) |
|
82 |
+ "Given a lens, a value and a rec, immutably update the rec to |
|
83 |
+contain the new value at the location focused by the lens." |
|
84 |
+ (unidentity |
|
85 |
+ (funcall (funcall lens (lambda (_) _ (wrap-identity v))) |
|
86 |
+ rec))) |
|
87 |
+ |
|
88 |
+#+fw.dev |
|
89 |
+(progn |
|
90 |
+ ;; "fake" functors that don't assume a functor result to their |
|
91 |
+ ;; callback |
|
92 |
+ (defun over* (lens cb rec) |
|
93 |
+ (funcall (funcall lens cb) |
|
94 |
+ rec)) |
|
95 |
+ |
|
96 |
+ (defun set* (lens value rec) |
|
97 |
+ (over lens |
|
98 |
+ (lambda (_) |
|
99 |
+ (declare (ignore _)) |
|
100 |
+ value) |
|
101 |
+ rec)) |
|
102 |
+ |
|
103 |
+ (defun view* (lens rec) |
|
104 |
+ (over lens |
|
105 |
+ (lambda (value) |
|
106 |
+ (return-from view* |
|
107 |
+ value)) |
|
108 |
+ rec))) |
|
109 |
+ |
|
110 |
+(defun make-alist-history-lens (key) |
|
111 |
+ "A lens for updating a alist, preserving previous values" |
|
112 |
+ (lambda (cb) |
|
113 |
+ (lambda (alist) |
|
114 |
+ (fmap (lambda (new) |
|
115 |
+ (cons (cons key new) |
|
116 |
+ alist)) |
|
117 |
+ (funcall cb (serapeum:assocdr key alist)))))) |
|
118 |
+ |
|
119 |
+(defun make-alist-lens (key) |
|
120 |
+ "A lens for updating a alist, discarding previous values" |
|
121 |
+ (lambda (cb) |
|
122 |
+ (lambda (alist) |
|
123 |
+ (fmap (lambda (new) |
|
124 |
+ (remove-duplicates (cons (cons key new) |
|
125 |
+ alist) |
|
126 |
+ :key #'car |
|
127 |
+ :from-end t)) |
|
128 |
+ (funcall cb (serapeum:assocdr key alist)))))) |
|
129 |
+ |
|
130 |
+(defun make-list-lens (index) |
|
131 |
+ "A lens for updating a sequence" |
|
132 |
+ (lambda (cb) |
|
133 |
+ (lambda (seq) |
|
134 |
+ (fmap (lambda (new) |
|
135 |
+ (let ((result (copy-seq seq))) |
|
136 |
+ (prog1 result |
|
137 |
+ (setf (elt result index) new)))) |
|
138 |
+ (funcall cb (elt seq index)))))) |
|
139 |
+ |
|
140 |
+(defun make-plist-lens (key) |
|
141 |
+ "A lens for updating a plist, preserving previous values" |
|
142 |
+ (lambda (cb) |
|
143 |
+ (lambda (plist) |
|
144 |
+ (fmap (lambda (new) |
|
145 |
+ (list* key new |
|
146 |
+ plist)) |
|
147 |
+ (funcall cb (getf plist key)))))) |
|
148 |
+ |
|
149 |
+(defun make-hash-table-lens (key) |
|
150 |
+ "A lens for updating a hash-table, discarding previous values" |
|
151 |
+ (lambda (cb) |
|
152 |
+ (lambda (old-hash) |
|
153 |
+ (fmap (lambda (new) |
|
154 |
+ (let ((new-hash (alexandria:copy-hash-table old-hash))) |
|
155 |
+ (prog1 new-hash |
|
156 |
+ (setf (gethash key new-hash) |
|
157 |
+ new)))) |
|
158 |
+ (funcall cb (gethash key old-hash)))))) |
|
159 |
+ |
|
160 |
+;; imagine a lens here that uses the MOP to immutably update a class... |
|
161 |
+(defgeneric clone (obj &rest new-initargs &key) |
|
162 |
+ (:method :around (obj &rest new-initargs &key) |
|
163 |
+ (apply #'reinitialize-instance (call-next-method) new-initargs))) |
|
164 |
+ |
|
165 |
+#+fw.demo |
|
166 |
+(progn |
|
167 |
+ (defclass foo () |
|
168 |
+ ((a :initarg :a :accessor a))) |
|
169 |
+ (defmethod clone ((obj foo) &key) |
|
170 |
+ (make-instance 'foo :a (a obj))) |
|
171 |
+ |
|
172 |
+ ;;; needs to be updated for functor-based lens |
|
173 |
+ (defun a-lens (cb) |
|
174 |
+ (lambda (foo) |
|
175 |
+ (fw.lu:prog1-bind (new (clone foo)) |
|
176 |
+ (setf (a new) |
|
177 |
+ (funcall cb (a foo)))))) |
|
178 |
+ (view 'a-lens |
|
179 |
+ (over 'a-lens '1+ |
|
180 |
+ (set 'a-lens 2 |
|
181 |
+ (make-instance 'foo :a 1)))) #| |
|
182 |
+ ==> 3 |#) |
|
183 |
+ |
|
184 |
+#+fw.dev |
|
185 |
+(progn |
|
186 |
+ ;; maybe functor implementation |
|
187 |
+ (defclass maybe () |
|
188 |
+ ()) |
|
189 |
+ (defclass just (maybe) |
|
190 |
+ ((%v :initarg :value :reader value))) |
|
191 |
+ (defclass nothing (maybe) |
|
192 |
+ ()) |
|
193 |
+ |
|
194 |
+ (defun just (value) |
|
195 |
+ (make-instance 'just :value value)) |
|
196 |
+ (defun nothing (&optional value) |
|
197 |
+ (declare (ignore value)) |
|
198 |
+ (make-instance 'nothing)) |
|
199 |
+ |
|
200 |
+ (defgeneric maybe (default value) |
|
201 |
+ (:method (default (value just)) |
|
202 |
+ (value value)) |
|
203 |
+ (:method (default (value nothing)) |
|
204 |
+ default)) |
|
205 |
+ |
|
206 |
+ (defgeneric maybe-apply (function value) |
|
207 |
+ (:method (function (value just)) |
|
208 |
+ (just (funcall function (value value)))) |
|
209 |
+ (:method (function (value nothing)) |
|
210 |
+ value)) |
|
211 |
+ |
|
212 |
+ (defmethod print-object ((o just) s) |
|
213 |
+ (format s "#.(~s ~s)" |
|
214 |
+ 'just |
|
215 |
+ (value o))) |
|
216 |
+ |
|
217 |
+ (defmethod print-object ((o nothing) s) |
|
218 |
+ (format s "#.(~s)" |
|
219 |
+ 'nothing))) |
0 | 220 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,22 @@ |
1 |
+(defpackage :data-lens.package |
|
2 |
+ (:use :cl ) |
|
3 |
+ (:export )) |
|
4 |
+(in-package :data-lens.package) |
|
5 |
+ |
|
6 |
+(defpackage :data-lens.lenses |
|
7 |
+ (:shadow :set) |
|
8 |
+ (:use :cl) |
|
9 |
+ (:export :over :set :view :make-alist-lens :make-plist-lens :make-hash-table-lens |
|
10 |
+ :make-list-lens)) |
|
11 |
+ |
|
12 |
+(defpackage :data-lens |
|
13 |
+ (:use :cl) |
|
14 |
+ (:import-from #:serapeum #:op #:defalias) |
|
15 |
+ (:export #:regex-match #:include #:exclude #:pick #:key-transform |
|
16 |
+ #:combine #:derive #:cumsum #:over #:on #:shortcut #:defun-ct #:key |
|
17 |
+ #:extract-key #:element #:let-fn #:juxt #:transform-tail #:slice |
|
18 |
+ #:compress-runs #:combine-matching-lists #:sorted #:applicable-when |
|
19 |
+ #:of-length #:of-min-length #:of-max-length #:transform-head |
|
20 |
+ #:maximizing #:zipping #:applying #:splice-elt #:transform-elt #:denest |
|
21 |
+ #:op #:defalias #:<> #:<>1 #:== #:• |
|
22 |
+ )) |