1
1
export @optic
2
2
export set, modify
3
3
export ∘ , opcompose, var"⨟"
4
- export Elements, Recursive, If, Properties
4
+ export Elements, Recursive, Query, If, Properties
5
5
export setproperties
6
6
export constructorof
7
7
using ConstructionBase
@@ -125,13 +125,40 @@ function _set(obj, optic, val, ::SetBased)
125
125
)
126
126
end
127
127
128
+ <<<<<< < HEAD
128
129
if VERSION < v " 1.7"
129
130
struct Returns{V}
130
131
value:: V
131
132
end
132
133
(o:: Returns )(x) = o. value
133
134
else
134
135
using Base: Returns
136
+ ====== =
137
+
138
+ struct Changed end
139
+ struct Unchanged end
140
+
141
+ struct MaybeConstruct end
142
+ _constructor (:: MaybeConstruct , :: Type{T} ) where T = constructorof (T)
143
+
144
+ struct List end
145
+ _constructor (:: List , :: Type ) = tuple
146
+
147
+ struct Splat end
148
+ _constructor (:: Splat , :: Type ) = _splat_all
149
+
150
+ _splat_all (args... ) = _splat_all (args)
151
+ @generated function _splat_all (args:: A ) where A<: Tuple
152
+ exp = Expr (:tuple )
153
+ for i in fieldnames (A)
154
+ push! (exp. args, Expr (:... , :(args[$ i])))
155
+ end
156
+ exp
157
+ end
158
+
159
+
160
+ struct Constant{V}
161
+ value:: V
135
162
end
136
163
137
164
@inline function _set (obj, optic, val, :: ModifyBased )
@@ -189,9 +216,7 @@ $EXPERIMENTAL
189
216
struct Elements end
190
217
OpticStyle (:: Type{<:Elements} ) = ModifyBased ()
191
218
192
- function modify (f, obj, :: Elements )
193
- map (f, obj)
194
- end
219
+ modify (f, obj, :: Elements ) = map (f, obj)
195
220
196
221
"""
197
222
If(modify_condition)
@@ -222,8 +247,36 @@ function modify(f, obj, w::If)
222
247
end
223
248
end
224
249
250
+ abstract type ObjectMap end
251
+
252
+ OpticStyle (:: Type{<:ObjectMap} ) = ModifyBased ()
253
+ modify (f, o, optic:: ObjectMap ) = mapobject (f, o, optic, Construct)
254
+
255
+ """
256
+ Properties()
257
+
258
+ Access all properties of an objects.
259
+
260
+ ```jldoctest
261
+ julia> using Accessors
262
+
263
+ julia> obj = (a=1, b=2, c=3)
264
+ (a = 1, b = 2, c = 3)
265
+
266
+ julia> set(obj, Properties(), "hi")
267
+ (a = "hi", b = "hi", c = "hi")
268
+
269
+ julia> modify(x -> 2x, obj, Properties())
270
+ (a = 2, b = 4, c = 6)
271
+ ```
272
+ Based on [`mapobject`](@ref).
273
+
274
+ $EXPERIMENTAL
275
+ """
276
+ struct Properties <: ObjectMap end
277
+
225
278
"""
226
- mapproperties (f, obj)
279
+ mapobject (f, obj)
227
280
228
281
Construct a copy of `obj`, with each property replaced by
229
282
the result of applying `f` to it.
@@ -233,7 +286,7 @@ julia> using Accessors
233
286
234
287
julia> obj = (a=1, b=2);
235
288
236
- julia> Accessors.mapproperties (x -> x+1, obj)
289
+ julia> Accessors.mapobject (x -> x+1, obj)
237
290
(a = 2, b = 3)
238
291
```
239
292
@@ -255,32 +308,20 @@ function mapproperties(f, obj)
255
308
nt = getproperties (obj)
256
309
patch = mapproperties (f, nt)
257
310
return setproperties (obj, patch)
258
- end
259
-
260
- """
261
- Properties()
262
-
263
- Access all properties of an objects.
264
311
265
- ```jldoctest
266
- julia> using Accessors
267
-
268
- julia> obj = (a=1, b=2, c=3)
269
- (a = 1, b = 2, c = 3)
270
-
271
- julia> set(obj, Properties(), "hi")
272
- (a = "hi", b = "hi", c = "hi")
273
-
274
- julia> modify(x -> 2x, obj, Properties())
275
- (a = 2, b = 4, c = 6)
276
- ```
277
- Based on [`mapproperties`](@ref).
312
+ # Don't construct when we don't absolutely have to.
313
+ # `constructorof` may not be defined for an object.
314
+ @generated function _maybeconstruct (obj:: O , props:: P , handler:: H ) where {O,P,H}
315
+ ctr = _constructor (H (), O)
316
+ if Changed in map (last ∘ fieldtypes, fieldtypes (P))
317
+ :($ ctr (map (first, props)... ) => Changed ())
318
+ else
319
+ :(obj => Unchanged ())
320
+ end
321
+ end
278
322
279
- $EXPERIMENTAL
280
- """
281
- struct Properties end
282
- OpticStyle (:: Type{<:Properties} ) = ModifyBased ()
283
- modify (f, o, :: Properties ) = mapproperties (f, o)
323
+ skip (:: Splat ) = true
324
+ skip (x) = false
284
325
285
326
"""
286
327
Recursive(descent_condition, optic)
@@ -318,6 +359,80 @@ function _modify(f, obj, r::Recursive, ::ModifyBased)
318
359
end
319
360
end
320
361
362
+ abstract type AbstractQuery end
363
+
364
+ """
365
+ Query(select, descend, optic)
366
+
367
+ Query an object recursively, choosing fields when `select`
368
+ returns `true`, and descending when `descend`.
369
+
370
+ ```jldoctest
371
+ julia> using Accessors
372
+
373
+ julia> obj = (a=missing, b=1, c=(d=missing, e=(f=missing, g=2)))
374
+ (a = missing, b = 1, c = (d = missing, e = (f = missing, g = 2)))
375
+
376
+ julia> set(obj, Query(ismissing), (1.0, 2.0, 3.0))
377
+ (a = 1.0, b = 1, c = (d = 2.0, e = (f = 3.jjjjjjtk,rg, g = 2)))
378
+
379
+ julia> obj = (1,2,(3,(4,5),6))
380
+ (1, 2, (3, (4, 5), 6))
381
+
382
+ julia> modify(x -> 100x, obj, Recursive(x -> (x isa Tuple), Elements()))
383
+ (100, 200, (300, (400, 500), 600))
384
+ ```
385
+ $EXPERIMENTAL
386
+ """
387
+ struct Query{Select,Descend,Optic<: Union{ComposedOptic,Properties} } <: AbstractQuery
388
+ select_condition:: Select
389
+ descent_condition:: Descend
390
+ optic:: Optic
391
+ end
392
+ Query (select, descend = x -> true ) = Query (select, descend, Properties ())
393
+ Query (; select= Any, descend= x -> true , optic= Properties ()) = Query (select, descend, optic)
394
+
395
+ OpticStyle (:: Type{<:AbstractQuery} ) = SetBased ()
396
+
397
+ @inline function (q:: AbstractQuery )(obj)
398
+ let obj= obj, q= q
399
+ mapobject (obj, _inner (q. optic), Splat ()) do o
400
+ if q. select_condition (o)
401
+ (_getouter (o, q. optic),)
402
+ elseif q. descent_condition (o)
403
+ q (o) # also a tuple
404
+ else
405
+ ()
406
+ end
407
+ end
408
+ end
409
+ end
410
+
411
+ set (obj, q:: AbstractQuery , vals) = _set (obj, q, (vals, 1 ))[1 ][1 ]
412
+
413
+ @inline function _set (obj, q:: AbstractQuery , (vals, itr))
414
+ let obj= obj, q= q, vals= vals, itr= itr
415
+ mapobject (obj, _inner (q. optic), MaybeConstruct (), itr) do o, itr:: Int
416
+ if q. select_condition (o)
417
+ _setouter (o, q. optic, vals[itr]) => Changed (), itr + 1
418
+ elseif q. descent_condition (o)
419
+ _set (o, q, (vals, itr)) # Will be marked as Changed()/Unchanged()
420
+ else
421
+ o => Unchanged (), itr
422
+ end
423
+ end
424
+ end
425
+ end
426
+
427
+ modify (f, obj, q:: Query ) = set (obj, q, map (f, q (obj)))
428
+
429
+ @inline _inner (optic:: ComposedOptic ) = optic. inner
430
+ @inline _inner (optic) = optic
431
+ @inline _getouter (o, optic:: ComposedOptic ) = optic. outer (o)
432
+ @inline _getouter (o, optic) = o
433
+ @inline _setouter (o, optic:: ComposedOptic , v) = set (o, optic. outer, v)
434
+ @inline _setouter (o, optic, v) = v
435
+
321
436
# ###############################################################################
322
437
# #### Lenses
323
438
# ###############################################################################
0 commit comments