@@ -129,24 +129,14 @@ end
129
129
struct Changed end
130
130
struct Unchanged end
131
131
132
- struct ConstructIfChanged{C}
133
- constructor:: C
134
- end
135
-
136
- # TODO what do we call these things?
137
- struct Construct end
138
- _constructor (:: Construct , :: Type{T} ) where T = constructorof (T)
139
-
140
132
struct MaybeConstruct end
141
- function _constructor (:: MaybeConstruct , :: Type{T} ) where T
142
- ConstructIfChanged (constructorof (T))
143
- end
133
+ _constructor (:: MaybeConstruct , :: Type{T} ) where T = constructorof (T)
144
134
145
135
struct List end
146
136
_constructor (:: List , :: Type ) = tuple
147
137
148
- struct Splat end
149
- _constructor (:: Splat , :: Type ) = _splat_all
138
+ struct Skip end
139
+ _constructor (:: Skip , :: Type ) = _splat_all
150
140
151
141
_splat_all (args... ) = _splat_all (args)
152
142
@generated function _splat_all (args:: A ) where A<: Tuple
@@ -297,7 +287,7 @@ function mapobject(f, obj::O, ::Properties, handler, itr::Nothing) where O
297
287
# TODO move this helper elsewhere?
298
288
pnames = propertynames (obj)
299
289
if isempty (pnames)
300
- return obj
290
+ return _maybeskip (handler, obj)
301
291
else
302
292
new_props = map (pnames) do p
303
293
f (getproperty (obj, p))
@@ -344,56 +334,69 @@ $EXPERIMENTAL
344
334
"""
345
335
struct Fields <: ObjectMap end
346
336
347
- @generated function mapobject (f, obj:: O , :: Fields , handler:: H = Construct () , itr:: I = nothing ) where {O,H,I}
337
+ @generated function mapobject (f, obj:: O , :: Fields , handler:: H , itr:: Nothing ) where {O,H,I}
348
338
# TODO : This is how Flatten.jl works, but it's not really
349
339
# correct use of ConstructionBase as it assumers properties=fields
350
340
fnames = fieldnames (O)
351
341
ctr = _constructor (H (), O)
352
342
if isempty (fnames)
353
- :(return _maybeitr (obj, itr ))
343
+ :(return _maybeskip (handler, obj ))
354
344
else
355
345
prop_args = map (fn -> :(getfield (obj, $ (QuoteNode (fn)))), fnames)
356
346
prop_exp = Expr (:tuple , prop_args... )
357
- if I === Nothing
358
- new_prop_exp = Expr (:tuple , map (pa -> :(f ($ pa)), prop_args)... )
359
- else
360
- # ## Unrolled iterating function appliation (it will compile away) ####
361
- # Each function call also updates the iterator value in local scoope with
362
- # the return value from the function. But it only actually inserts the
363
- # value into the parent tuple.
364
- val_exps = map (prop_args) do pa
365
- :((val, itr) = f ($ pa, itr); val)
366
- end
367
- new_prop_exp = Expr (:tuple , val_exps... )
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... )
368
352
end
369
- ret = if H == MaybeConstruct
370
- quote
371
- # TODO : last type instability.
372
- # replace this with val => Changed(), val => Unchanged()
373
- # return values.
374
- #
375
- # Don't construct when we don't absolutely have to.
376
- # `constructorof` may not be defined for an object.
377
- if props === new_props
378
- return _maybeitr (obj, itr)
379
- else
380
- return _maybeitr ($ ctr (new_props... ), itr)
381
- end
382
- end
383
- else
384
- ret = :(return _maybeitr ($ ctr (new_props... ), itr))
353
+ end
354
+ end
355
+ @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
+ fnames = fieldnames (O)
359
+ ctr = _constructor (H (), O)
360
+ if isempty (fnames)
361
+ :(return (obj, itr) => Unchanged ())
362
+ 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)
385
371
end
372
+ new_prop_exp = Expr (:tuple , val_exps... )
386
373
quote
387
374
props = $ prop_exp
388
375
new_props = $ new_prop_exp
389
- $ ret
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
390
384
end
391
385
end
392
386
end
393
387
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
394
_maybeitr (x, :: Nothing ) = x
395
395
_maybeitr (x, itr) = x, itr
396
396
397
+ _maybeskip (:: Skip , v) = ()
398
+ _maybeskip (x, v) = v
399
+
397
400
"""
398
401
Recursive(descent_condition, optic)
399
402
@@ -499,7 +502,7 @@ Query(select, descend = x -> true) = Query(select, descend, Fields())
499
502
Query (; select= Any, descend= x -> true , optic= Fields ()) = Query (select, descend, optic)
500
503
501
504
function (q:: Query )(obj)
502
- mapobject (obj, _inner (q. optic), Splat (), nothing ) do o
505
+ mapobject (obj, _inner (q. optic), Skip (), nothing ) do o
503
506
if q. select_condition (o)
504
507
(_getouter (o, q. optic),)
505
508
elseif q. descent_condition (o)
@@ -510,16 +513,16 @@ function (q::Query)(obj)
510
513
end
511
514
end
512
515
513
- set (obj, q:: Query , vals) = _set (obj, q:: Query , (vals, 1 ))[1 ]
516
+ set (obj, q:: Query , vals) = _set (obj, q:: Query , (vals, 1 ))[1 ][ 1 ]
514
517
515
518
function _set (obj, q:: Query , (vals, itr))
516
- mapobject (obj, _inner (q. optic), Construct (), itr) do o, itr
519
+ mapobject (obj, _inner (q. optic), MaybeConstruct (), itr) do o, itr
517
520
if q. select_condition (o)
518
- _setouter (o, q. optic, vals[itr]), itr + 1
521
+ ( _setouter (o, q. optic, vals[itr]), itr + 1 ) => Changed ()
519
522
elseif q. descent_condition (o)
520
523
_set (o, q, (vals, itr))
521
524
else
522
- o, itr
525
+ ( o, itr) => Unchanged ()
523
526
end
524
527
end
525
528
end
0 commit comments