@@ -6,6 +6,7 @@ export setproperties
66export constructorof
77using ConstructionBase
88using CompositionsBase
9+ using Static
910using Base: getproperty
1011using Base
1112
@@ -125,15 +126,15 @@ function _set(obj, optic, val, ::SetBased)
125126 )
126127end
127128
128- <<<<<< < HEAD
129129if VERSION < v " 1.7"
130130 struct Returns{V}
131131 value:: V
132132 end
133133 (o:: Returns )(x) = o. value
134134else
135135 using Base: Returns
136- ====== =
136+ end
137+
137138
138139struct Changed end
139140struct Unchanged end
@@ -276,19 +277,7 @@ $EXPERIMENTAL
276277struct Properties <: ObjectMap end
277278
278279"""
279- mapobject(f, obj)
280-
281- Construct a copy of `obj`, with each property replaced by
282- the result of applying `f` to it.
283-
284- ```jldoctest
285- julia> using Accessors
286-
287- julia> obj = (a=1, b=2);
288-
289- julia> Accessors.mapobject(x -> x+1, obj)
290- (a = 2, b = 3)
291- ```
280+ maproperties()
292281
293282# Implementation
294283
@@ -300,8 +289,8 @@ $EXPERIMENTAL
300289"""
301290function mapproperties end
302291
303- function mapproperties (f, nt:: NamedTuple )
304- map (f,nt)
292+ function mapproperties (f, nt:: Union{Tuple, NamedTuple} )
293+ map (f, nt)
305294end
306295
307296function mapproperties (f, obj)
@@ -310,17 +299,6 @@ function mapproperties(f, obj)
310299 return setproperties (obj, patch)
311300end
312301
313- # Don't construct when we don't absolutely have to.
314- # `constructorof` may not be defined for an object.
315- @generated function _maybeconstruct (obj:: O , props:: P , handler:: H ) where {O,P,H}
316- ctr = _constructor (H (), O)
317- if Changed in map (last ∘ fieldtypes, fieldtypes (P))
318- :($ ctr (map (first, props)... ) => Changed ())
319- else
320- :(obj => Unchanged ())
321- end
322- end
323-
324302skip (:: Splat ) = true
325303skip (x) = false
326304
@@ -360,6 +338,59 @@ function _modify(f, obj, r::Recursive, ::ModifyBased)
360338 end
361339end
362340
341+ """
342+
343+ new_obj, new_state = modify_stateful(f, (obj,state), optic)
344+
345+ Here `f` has signature `f(::Value, ::State) -> Tuple{NewValue, NewState}`.
346+ """
347+ function modify_stateful end
348+
349+ @inline function modify_stateful (f, (obj, state), optic:: Properties )
350+ let f= f, obj= obj, state= state
351+ modify_stateful_context ((obj, state), optic) do _, fn, pr, st
352+ f (getfield (pr, known (fn)), st)
353+ end
354+ end
355+ end
356+
357+ @generated function modify_stateful_context (f, (obj, state1):: T , optic:: Properties ) where T
358+ _modify_stateful_inner (T)
359+ end
360+
361+ # Separated for testing object/state combinations without restarts
362+ function _modify_stateful_inner (:: Type{<:Tuple{O,S}} ) where {O,S}
363+ modifications = []
364+ vals = Expr (:tuple )
365+ fns = fieldnames (O)
366+ local st1 = :state0
367+ local st2 = :state1
368+ for (i, fn) in enumerate (fns)
369+ v = Symbol (" val$i " )
370+ st1 = Symbol (" state$i " )
371+ st2 = Symbol (" state$(i+ 1 ) " )
372+ ms = if O <: Tuple
373+ :(($ v, $ st2) = f (obj, StaticInt {$(QuoteNode(fn))} (), props, $ st1))
374+ else
375+ :(($ v, $ st2) = f (obj, StaticSymbol {$(QuoteNode(fn))} (), props, $ st1))
376+ end
377+ push! (modifications, ms)
378+ push! (vals. args, v)
379+ end
380+ patch = O <: Tuple ? vals : :(NamedTuple {$fns} ($ vals))
381+ Expr (:block ,
382+ :(props = getproperties (obj)),
383+ modifications... ,
384+ :(patch = $ patch),
385+ :(new_obj = maybesetproperties ($ st2, obj, patch)),
386+ :(new_state = maybesetstate ($ st2, obj, patch)),
387+ :(return (setproperties (obj, patch), $ st2)),
388+ )
389+ end
390+
391+ maybesetproperties (state, obj, patch) = setproperties (obj, patch)
392+ maybesetstate (state, obj, patch) = state
393+
363394abstract type AbstractQuery end
364395
365396"""
@@ -395,44 +426,101 @@ Query(; select=Any, descend=x -> true, optic=Properties()) = Query(select, desce
395426
396427OpticStyle (:: Type{<:AbstractQuery} ) = SetBased ()
397428
398- @inline function (q:: AbstractQuery )(obj)
399- let obj= obj, q= q
400- mapobject (obj, _inner (q. optic), Splat ()) do o
401- if q. select_condition (o)
402- (_getouter (o, q. optic),)
403- elseif q. descent_condition (o)
404- q (o) # also a tuple
405- else
406- ()
407- end
408- end
429+ struct Context{Select,Descend,Optic<: Union{ComposedOptic,Properties} } <: AbstractQuery
430+ select_condition:: Select
431+ descent_condition:: Descend
432+ optic:: Optic
433+ end
434+
435+
436+ struct ContextState{V}
437+ vals:: V
438+ end
439+ struct GetAllState{V}
440+ vals:: V
441+ end
442+ struct SetAllState{C,V,I}
443+ change:: C
444+ vals:: V
445+ itr:: I
446+ end
447+
448+ pop (x) = first (x), Base. tail (x)
449+ push (x, val) = (x... , val)
450+ push (x:: GetAllState , val) = GetAllState (push (x. vals, val))
451+
452+ (q:: Query )(obj) = getall (obj, q)
453+
454+ function getall (obj, q)
455+ initial_state = GetAllState (())
456+ _, final_state = modify_stateful ((obj, initial_state), q) do o, s
457+ new_state = push (s, outer (q. optic, o, s))
458+ o, new_state
459+ end
460+ return final_state. vals
461+ end
462+
463+ function setall (obj, q, vals)
464+ initial_state = SetAllState (Unchanged (), vals, 1 )
465+ final_obj, _ = modify_stateful ((obj, initial_state), q) do o, s
466+ new_output = outer (q. optic, o, s)
467+ new_state = SetAllState (Changed (), s. vals, s. itr + 1 )
468+ new_output, new_state
469+ end
470+ return final_obj
471+ end
472+
473+ function context (f, obj, q)
474+ initial_state = GetAllState (())
475+ _, final_state = modify_stateful_context ((obj, initial_state), Properties ()) do o, fn, pr, s
476+ new_state = push (s, f (o, known (fn)))
477+ o, new_state
409478 end
479+ return final_state. vals
410480end
411481
412- set ( obj, q:: AbstractQuery , vals ) = _set (obj, q, (vals, 1 ))[ 1 ][ 1 ]
482+ modify (f, obj, q:: Query ) = setall (obj, q, map (f, getall (obj, q)))
413483
414- @inline function _set (obj, q :: AbstractQuery , (vals, itr))
415- let obj= obj, q = q, vals = vals, itr = itr
416- mapobject (obj, _inner (q . optic), MaybeConstruct (), itr) do o, itr :: Int
417- if q . select_condition (o )
418- _setouter (o, q . optic, vals[itr]) => Changed (), itr + 1
419- elseif q . descent_condition (o )
420- _set (o, q, (vals, itr)) # Will be marked as Changed()/Unchanged( )
421- else
422- o => Unchanged (), itr
423- end
484+ @inline function modify_stateful (f :: F , (obj, state), q :: Query ) where F
485+ modify_stateful (( obj, state), inner (q . optic)) do o, s
486+ if q . select_condition (o)
487+ f (o, s )
488+ elseif q . descent_condition (o)
489+ ds = descent_state (s )
490+ o, s = modify_stateful (f :: F , (o, ds), q )
491+ o, merge_state (s, ds)
492+ else
493+ o, s
424494 end
425495 end
426496end
427497
428- modify (f, obj, q:: Query ) = set (obj, q, map (f, q (obj)))
498+ maybesetproperties (state:: GetAllState , obj, patch) = obj
499+ maybesetproperties (state:: SetAllState , obj, patch) =
500+ maybesetproperties (state. change, state, obj, patch)
501+ maybesetproperties (:: Changed , state:: SetAllState , obj, patch) = setproperties (obj, patch)
502+ maybesetproperties (:: Unchanged , state:: SetAllState , obj, patch) = obj
503+
504+ descent_state (state:: SetAllState ) = SetAllState (Unchanged (), state. vals, state. itr)
505+ descent_state (state) = state
506+
507+ merge_state (s1:: SetAllState , s2) = SetAllState (anychanged (s1, s2), s2. vals, s2. itr)
508+ merge_state (s1, s2) = s2
509+
510+ anychanged (s1, s2) = anychanged (s1. change, s2. change)
511+ anychanged (:: Unchanged , :: Unchanged ) = Unchanged ()
512+ anychanged (:: Unchanged , :: Changed ) = Changed ()
513+ anychanged (:: Changed , :: Unchanged ) = Changed ()
514+ anychanged (:: Changed , :: Changed ) = Changed ()
515+
516+ inner (optic) = optic
517+ inner (optic:: ComposedOptic ) = optic. inner
518+
519+ outer (optic, o, state:: GetAllState ) = o
520+ outer (optic:: ComposedOptic , o, state:: GetAllState ) = optic. outer (o)
521+ outer (optic:: ComposedOptic , o, state:: SetAllState ) = set (o, optic. outer, state. vals[state. itr])
522+ outer (optic, o, state:: SetAllState ) = state. vals[state. itr]
429523
430- @inline _inner (optic:: ComposedOptic ) = optic. inner
431- @inline _inner (optic) = optic
432- @inline _getouter (o, optic:: ComposedOptic ) = optic. outer (o)
433- @inline _getouter (o, optic) = o
434- @inline _setouter (o, optic:: ComposedOptic , v) = set (o, optic. outer, v)
435- @inline _setouter (o, optic, v) = v
436524
437525# ###############################################################################
438526# #### Lenses
0 commit comments