@@ -135,8 +135,8 @@ _constructor(::MaybeConstruct, ::Type{T}) where T = constructorof(T)
135
135
struct List end
136
136
_constructor (:: List , :: Type ) = tuple
137
137
138
- struct Skip end
139
- _constructor (:: Skip , :: Type ) = _splat_all
138
+ struct Splat end
139
+ _constructor (:: Splat , :: Type ) = _splat_all
140
140
141
141
_splat_all (args... ) = _splat_all (args)
142
142
@generated function _splat_all (args:: A ) where A<: Tuple
242
242
abstract type ObjectMap end
243
243
244
244
OpticStyle (:: Type{<:ObjectMap} ) = ModifyBased ()
245
- modify (f, o, optic:: ObjectMap ) = mapobject (f, o, optic, Construct, nothing )
245
+ modify (f, o, optic:: ObjectMap ) = mapobject (f, o, optic, Construct)
246
246
247
247
"""
248
248
Properties()
@@ -283,17 +283,17 @@ julia> Accessors.mapobject(x -> x+1, obj)
283
283
```
284
284
$EXPERIMENTAL
285
285
"""
286
- function mapobject (f, obj:: O , :: Properties , handler, itr :: Nothing ) where O
286
+ function mapobject (f, obj:: O , :: Properties , handler) where O
287
287
# TODO move this helper elsewhere?
288
288
pnames = propertynames (obj)
289
289
if isempty (pnames)
290
- return _maybeskip (handler, obj)
290
+ return skip (handler) ? () : obj
291
291
else
292
- new_props = map (pnames) do p
292
+ ctr = _constructor (handler, O)
293
+ args = map (pnames) do p
293
294
f (getproperty (obj, p))
294
295
end
295
- ctr = _constructor (handler, O)
296
- return ctr (new_props... )
296
+ return ctr (args... )
297
297
end
298
298
end
299
299
function mapobject (f, obj:: O , :: Properties , handler, itr:: Int ) where O
@@ -334,68 +334,50 @@ $EXPERIMENTAL
334
334
"""
335
335
struct Fields <: ObjectMap end
336
336
337
- @generated function mapobject (f, obj:: O , :: Fields , handler:: H , itr :: Nothing ) where {O,H,I}
337
+ @generated function mapobject (f, obj:: O , :: Fields , handler:: H ) where {O,H,I}
338
338
# TODO : This is how Flatten.jl works, but it's not really
339
339
# correct use of ConstructionBase as it assumers properties=fields
340
340
fnames = fieldnames (O)
341
341
ctr = _constructor (H (), O)
342
342
if isempty (fnames)
343
- :( return _maybeskip (handler, obj) )
343
+ skip ( H ()) ? :(()) : :( obj)
344
344
else
345
- prop_args = map (fn -> :(getfield (obj, $ (QuoteNode (fn)))), fnames)
346
- prop_exp = Expr (:tuple , prop_args... )
347
- new_prop_exp = Expr (:tuple , map (pa -> :(f ($ pa)), prop_args)... )
348
- quote
349
- props = $ prop_exp
350
- new_props = $ new_prop_exp
351
- return $ ctr (new_props... )
345
+ args = map (fnames) do fn
346
+ :(f (getfield (obj, $ (QuoteNode (fn)))))
352
347
end
348
+ args_exp = Expr (:tuple , args... )
349
+ return :($ ctr ($ args_exp... ))
353
350
end
354
351
end
355
352
@generated function mapobject (f, obj:: O , :: Fields , handler:: H , itr:: Int ) where {O,H}
356
- # TODO : This is how Flatten.jl works, but it's not really
357
- # correct use of ConstructionBase as it assumers properties=fields
358
353
fnames = fieldnames (O)
359
- ctr = _constructor (H (), O)
360
354
if isempty (fnames)
361
- :(return ( obj, itr) => Unchanged ())
355
+ :(obj => Unchanged (), itr)
362
356
else
363
- prop_args = map (fn -> :(getfield (obj, $ (QuoteNode (fn)))), fnames)
364
- prop_exp = Expr (:tuple , prop_args... )
365
- # ## Unrolled iterating function appliation (it will compile away) ####
366
- # Each function call also updates the iterator value in local scoope with
367
- # the return value from the function. But it only actually inserts the
368
- # value into the parent tuple.
369
- val_exps = map (prop_args) do pa
370
- :(((val, itr), change) = f ($ pa, itr); val => change)
371
- end
372
- new_prop_exp = Expr (:tuple , val_exps... )
373
- quote
374
- props = $ prop_exp
375
- new_props = $ new_prop_exp
376
- new_props, change = _splitchanged (new_props)
377
- # Don't construct when we don't absolutely have to.
378
- # `constructorof` may not be defined for an object.
379
- if change isa Changed
380
- return ($ ctr (new_props... ), itr) => change
381
- else
382
- return (obj, itr) => change
383
- end
357
+ # ## Unrolled iterating function appliation ####
358
+ # Each function call updates the iterator value in
359
+ # local scoope with its return value
360
+ args = map (fnames) do fn
361
+ :((val, itr) = f (getfield (obj, $ (QuoteNode (fn))), itr); val)
384
362
end
363
+ args_exp = Expr (:tuple , args... )
364
+ return :(_maybeconstruct (obj, $ args_exp, handler), itr)
385
365
end
386
366
end
387
367
388
- _splitchanged (props) = map (first, props), _findchanged (map (last, props))
389
-
390
- _findchanged (:: Tuple{Changed,Vararg} ) = Changed ()
391
- _findchanged (cs:: Tuple ) = _findchanged (Base. tail (cs))
392
- _findchanged (:: Tuple{} ) = Unchanged ()
393
-
394
- _maybeitr (x, :: Nothing ) = x
395
- _maybeitr (x, itr) = x, itr
368
+ # Don't construct when we don't absolutely have to.
369
+ # `constructorof` may not be defined for an object.
370
+ @generated function _maybeconstruct (obj:: O , props:: P , handler:: H ) where {O,P,H}
371
+ ctr = _constructor (H (), O)
372
+ if Changed in map (last ∘ fieldtypes, fieldtypes (P))
373
+ :($ ctr (map (first, props)... ) => Changed ())
374
+ else
375
+ :(obj => Unchanged ())
376
+ end
377
+ end
396
378
397
- _maybeskip (:: Skip , v ) = ()
398
- _maybeskip (x, v ) = v
379
+ skip (:: Splat ) = true
380
+ skip (x ) = false
399
381
400
382
"""
401
383
Recursive(descent_condition, optic)
@@ -433,45 +415,8 @@ function _modify(f, obj, r::Recursive, ::ModifyBased)
433
415
end
434
416
end
435
417
436
- # ###############################################################################
437
- # #### Lenses
438
- # ###############################################################################
439
- struct PropertyLens{fieldname} end
440
-
441
- function (l:: PropertyLens{field} )(obj) where {field}
442
- getproperty (obj, field)
443
- end
444
-
445
- @inline function set (obj, l:: PropertyLens{field} , val) where {field}
446
- patch = (;field => val)
447
- setproperties (obj, patch)
448
- end
449
-
450
- struct IndexLens{I <: Tuple }
451
- indices:: I
452
- end
453
-
454
- Base. @propagate_inbounds function (lens:: IndexLens )(obj)
455
- getindex (obj, lens. indices... )
456
- end
457
- Base. @propagate_inbounds function set (obj, lens:: IndexLens , val)
458
- setindex (obj, val, lens. indices... )
459
- end
460
-
461
- struct DynamicIndexLens{F}
462
- f:: F
463
- end
464
-
465
- Base. @propagate_inbounds function (lens:: DynamicIndexLens )(obj)
466
- return obj[lens. f (obj)... ]
467
- end
468
-
469
- Base. @propagate_inbounds function set (obj, lens:: DynamicIndexLens , val)
470
- return setindex (obj, val, lens. f (obj)... )
471
- end
472
-
473
418
"""
474
- Query(select, descend)
419
+ Query(select, descend, optic )
475
420
476
421
Query an object recursively, choosing fields when `select`
477
422
returns `true`, and descending when `descend`.
501
446
Query (select, descend = x -> true ) = Query (select, descend, Fields ())
502
447
Query (; select= Any, descend= x -> true , optic= Fields ()) = Query (select, descend, optic)
503
448
449
+ OpticStyle (:: Type{<:Query} ) = SetBased ()
450
+
504
451
function (q:: Query )(obj)
505
- mapobject (obj, _inner (q. optic), Skip (), nothing ) do o
452
+ mapobject (obj, _inner (q. optic), Splat () ) do o
506
453
if q. select_condition (o)
507
454
(_getouter (o, q. optic),)
508
455
elseif q. descent_condition (o)
@@ -518,11 +465,11 @@ set(obj, q::Query, vals) = _set(obj, q::Query, (vals, 1))[1][1]
518
465
function _set (obj, q:: Query , (vals, itr))
519
466
mapobject (obj, _inner (q. optic), MaybeConstruct (), itr) do o, itr
520
467
if q. select_condition (o)
521
- ( _setouter (o, q. optic, vals[itr]), itr + 1 ) => Changed ()
468
+ _setouter (o, q. optic, vals[itr]) => Changed (), itr + 1
522
469
elseif q. descent_condition (o)
523
- _set (o, q, (vals, itr))
470
+ _set (o, q, (vals, itr)) # Will be marked as Changed()/Unchanged()
524
471
else
525
- (o, itr) => Unchanged ()
472
+ o => Unchanged (), itr
526
473
end
527
474
end
528
475
end
@@ -535,3 +482,40 @@ _getouter(o, optic::ComposedOptic) = optic.outer(o)
535
482
_getouter (o, optic) = o
536
483
_setouter (o, optic:: ComposedOptic , v) = set (o, optic. outer, v)
537
484
_setouter (o, optic, v) = v
485
+
486
+ # ###############################################################################
487
+ # #### Lenses
488
+ # ###############################################################################
489
+ struct PropertyLens{fieldname} end
490
+
491
+ function (l:: PropertyLens{field} )(obj) where {field}
492
+ getproperty (obj, field)
493
+ end
494
+
495
+ @inline function set (obj, l:: PropertyLens{field} , val) where {field}
496
+ patch = (;field => val)
497
+ setproperties (obj, patch)
498
+ end
499
+
500
+ struct IndexLens{I <: Tuple }
501
+ indices:: I
502
+ end
503
+
504
+ Base. @propagate_inbounds function (lens:: IndexLens )(obj)
505
+ getindex (obj, lens. indices... )
506
+ end
507
+ Base. @propagate_inbounds function set (obj, lens:: IndexLens , val)
508
+ setindex (obj, val, lens. indices... )
509
+ end
510
+
511
+ struct DynamicIndexLens{F}
512
+ f:: F
513
+ end
514
+
515
+ Base. @propagate_inbounds function (lens:: DynamicIndexLens )(obj)
516
+ return obj[lens. f (obj)... ]
517
+ end
518
+
519
+ Base. @propagate_inbounds function set (obj, lens:: DynamicIndexLens , val)
520
+ return setindex (obj, val, lens. f (obj)... )
521
+ end
0 commit comments