@@ -129,24 +129,14 @@ end
129129struct Changed end
130130struct Unchanged end
131131
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-
140132struct 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)
144134
145135struct List end
146136_constructor (:: List , :: Type ) = tuple
147137
148- struct Splat end
149- _constructor (:: Splat , :: Type ) = _splat_all
138+ struct Skip end
139+ _constructor (:: Skip , :: Type ) = _splat_all
150140
151141_splat_all (args... ) = _splat_all (args)
152142@generated function _splat_all (args:: A ) where A<: Tuple
@@ -297,7 +287,7 @@ function mapobject(f, obj::O, ::Properties, handler, itr::Nothing) where O
297287 # TODO move this helper elsewhere?
298288 pnames = propertynames (obj)
299289 if isempty (pnames)
300- return obj
290+ return _maybeskip (handler, obj)
301291 else
302292 new_props = map (pnames) do p
303293 f (getproperty (obj, p))
@@ -344,56 +334,69 @@ $EXPERIMENTAL
344334"""
345335struct Fields <: ObjectMap end
346336
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}
348338 # TODO : This is how Flatten.jl works, but it's not really
349339 # correct use of ConstructionBase as it assumers properties=fields
350340 fnames = fieldnames (O)
351341 ctr = _constructor (H (), O)
352342 if isempty (fnames)
353- :(return _maybeitr (obj, itr ))
343+ :(return _maybeskip (handler, obj ))
354344 else
355345 prop_args = map (fn -> :(getfield (obj, $ (QuoteNode (fn)))), fnames)
356346 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... )
368352 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)
385371 end
372+ new_prop_exp = Expr (:tuple , val_exps... )
386373 quote
387374 props = $ prop_exp
388375 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
390384 end
391385 end
392386end
393387
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+
394394_maybeitr (x, :: Nothing ) = x
395395_maybeitr (x, itr) = x, itr
396396
397+ _maybeskip (:: Skip , v) = ()
398+ _maybeskip (x, v) = v
399+
397400"""
398401 Recursive(descent_condition, optic)
399402
@@ -499,7 +502,7 @@ Query(select, descend = x -> true) = Query(select, descend, Fields())
499502Query (; select= Any, descend= x -> true , optic= Fields ()) = Query (select, descend, optic)
500503
501504function (q:: Query )(obj)
502- mapobject (obj, _inner (q. optic), Splat (), nothing ) do o
505+ mapobject (obj, _inner (q. optic), Skip (), nothing ) do o
503506 if q. select_condition (o)
504507 (_getouter (o, q. optic),)
505508 elseif q. descent_condition (o)
@@ -510,16 +513,16 @@ function (q::Query)(obj)
510513 end
511514end
512515
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 ]
514517
515518function _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
517520 if q. select_condition (o)
518- _setouter (o, q. optic, vals[itr]), itr + 1
521+ ( _setouter (o, q. optic, vals[itr]), itr + 1 ) => Changed ()
519522 elseif q. descent_condition (o)
520523 _set (o, q, (vals, itr))
521524 else
522- o, itr
525+ ( o, itr) => Unchanged ()
523526 end
524527 end
525528end
0 commit comments