git.fiddlerwoaroof.com
Browse code

chore: split into files

Ed L authored on 06/10/2020 02:44:35
Showing 4 changed files
... ...
@@ -9,5 +9,6 @@
9 9
                :alexandria
10 10
                :serapeum)
11 11
   :serial t
12
-  :components ((:file "lens")))
13
-
12
+  :components ((:file "package")
13
+               (:file "optics")
14
+               (:file "lens")))
... ...
@@ -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
+           ))