11export @optic
22export set, modify
33export ∘ , opcompose, var"⨟"
4- export Elements, Recursive, If, Properties
4+ export Elements, Recursive, Query, If, Properties
55export setproperties
66export constructorof
77using ConstructionBase
@@ -125,13 +125,40 @@ function _set(obj, optic, val, ::SetBased)
125125 )
126126end
127127
128+ <<<<<< < HEAD
128129if VERSION < v " 1.7"
129130 struct Returns{V}
130131 value:: V
131132 end
132133 (o:: Returns )(x) = o. value
133134else
134135 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
135162end
136163
137164@inline function _set (obj, optic, val, :: ModifyBased )
@@ -189,9 +216,7 @@ $EXPERIMENTAL
189216struct Elements end
190217OpticStyle (:: Type{<:Elements} ) = ModifyBased ()
191218
192- function modify (f, obj, :: Elements )
193- map (f, obj)
194- end
219+ modify (f, obj, :: Elements ) = map (f, obj)
195220
196221"""
197222 If(modify_condition)
@@ -222,8 +247,36 @@ function modify(f, obj, w::If)
222247 end
223248end
224249
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+
225278"""
226- mapproperties (f, obj)
279+ mapobject (f, obj)
227280
228281Construct a copy of `obj`, with each property replaced by
229282the result of applying `f` to it.
@@ -233,7 +286,7 @@ julia> using Accessors
233286
234287julia> obj = (a=1, b=2);
235288
236- julia> Accessors.mapproperties (x -> x+1, obj)
289+ julia> Accessors.mapobject (x -> x+1, obj)
237290(a = 2, b = 3)
238291```
239292
@@ -255,32 +308,20 @@ function mapproperties(f, obj)
255308 nt = getproperties (obj)
256309 patch = mapproperties (f, nt)
257310 return setproperties (obj, patch)
258- end
259-
260- """
261- Properties()
262-
263- Access all properties of an objects.
264311
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
278322
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
284325
285326"""
286327 Recursive(descent_condition, optic)
@@ -318,6 +359,80 @@ function _modify(f, obj, r::Recursive, ::ModifyBased)
318359 end
319360end
320361
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+
321436# ###############################################################################
322437# #### Lenses
323438# ###############################################################################
0 commit comments