Skip to content

Commit 3f11d5a

Browse files
committed
Add AnonymousWalk and update docstrings
1 parent e051e21 commit 3f11d5a

File tree

4 files changed

+108
-6
lines changed

4 files changed

+108
-6
lines changed

docs/src/api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ Functors.children
99
Functors.isleaf
1010
```
1111

12+
```@docs
13+
Functors.AbstractWalk
14+
Functors.DefaultWalk
15+
Functors.StructuralWalk
16+
Functors.ExcludeWalk
17+
Functors.CachedWalk
18+
Functors.CollectWalk
19+
```
20+
1221
```@docs
1322
Functors.fmapstructure
1423
Functors.fcollect

src/Functors.jl

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ Equivalent to `functor(x)[1]`.
104104
children
105105

106106
"""
107-
fmap(f, x; exclude = Functors.isleaf, walk = Functors._default_walk)
107+
fmap(f, x, ys...; exclude = Functors.isleaf, walk = Functors.DefaultWalk()[, prune])
108+
fmap(walk, f, x, ys...)
108109
109110
A structure and type preserving `map`.
110111
@@ -178,12 +179,23 @@ Foo("Bar([1, 2, 3])", (4, 5, "Bar(Foo(6, 7))"))
178179
To recurse into custom types without reconstructing them afterwards,
179180
use [`fmapstructure`](@ref).
180181
181-
For advanced customization of the traversal behaviour, pass a custom `walk` function of the form `(f', xs) -> ...`.
182-
This function walks (maps) over `xs` calling the continuation `f'` to continue traversal.
182+
For advanced customization of the traversal behaviour,
183+
pass a custom `walk` function that subtypes [`Functors.AbstractWalk`](ref).
184+
The form `fmap(walk, f, x, ys...)` can be called for custom walks.
185+
The simpler form `fmap(f, x, ys...; walk = mywalk)` will wrap `mywalk` in
186+
[`ExcludeWalk`](@ref) then [`CachedWalk`](@ref).
183187
184188
```jldoctest withfoo
185-
julia> fmap(x -> 10x, m, walk=(f, x) -> x isa Bar ? x : Functors._default_walk(f, x))
186-
Foo(Bar([1, 2, 3]), (40, 50, Bar(Foo(6, 7))))
189+
julia> struct MyWalk <: Functors.AbstractWalk end
190+
191+
julia> (::MyWalk)(recurse, x) = x isa Bar ? "hello" :
192+
Functors.DefaultWalk()(recurse, x)
193+
194+
julia> fmap(x -> 10x, m; walk = MyWalk())
195+
Foo("hello", (40, 50, "hello"))
196+
197+
julia> fmap(MyWalk(), x -> 10x, m)
198+
Foo("hello", (4, 5, "hello"))
187199
```
188200
189201
The behaviour when the same node appears twice can be altered by giving a value

src/maps.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function fmap(f, x, ys...; exclude = isleaf,
44
walk = DefaultWalk(),
55
cache = IdDict(),
66
prune = NoKeyword())
7-
_walk = CachedWalk(ExcludeWalk(walk, f, exclude), prune, cache)
7+
_walk = CachedWalk(ExcludeWalk(AnonymousWalk(walk), f, exclude), prune, cache)
88
fmap(_walk, f, x, ys...)
99
end
1010

src/walks.jl

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,48 @@
1+
"""
2+
AbstractWalk
3+
4+
Any walk for use with [`fmap`](@ref) should inherit from this type.
5+
A walk subtyping `AbstractWalk` must satisfy the walk function interface:
6+
```julia
7+
struct MyWalk <: AbstractWalk end
8+
9+
function (::MyWalk)(recurse, x, ys...)
10+
# implement this
11+
end
12+
```
13+
The walk function is called on a node `x` in a Functors tree.
14+
It may also be passed associated nodes `ys...` in other Functors trees.
15+
The walk function recurses further into `(x, ys...)` by calling
16+
`recurse` on the child nodes.
17+
The choice of which nodes to recurse and in what order is custom to the walk.
18+
"""
119
abstract type AbstractWalk end
220

21+
"""
22+
AnonymousWalk(walk_fn)
23+
24+
Wrap a `walk_fn` so that `AnonymousWalk(walk_fn) isa AbstractWalk`.
25+
This type only exists for backwards compatability and should be directly used.
26+
Attempting to wrap an existing `AbstractWalk` is a no-op (i.e. it is not wrapped).
27+
"""
28+
struct AnonymousWalk{F} <: AbstractWalk
29+
walk::F
30+
end
31+
# do not wrap an AbstractWalk
32+
AnonymousWalk(walk::AbstractWalk) = walk
33+
34+
(walk::AnonymousWalk)(recurse, x, ys...) = walk.walk(recurse, x, ys...)
35+
36+
"""
37+
DefaultWalk()
38+
39+
The default walk behavior for Functors.jl.
40+
Walks all the [`Functors.children`](@ref) of trees `(x, ys...)` based on
41+
the structure of `x`.
42+
The resulting mapped child nodes are restructured into the type of `x`.
43+
44+
See [`fmap`](@ref) for more information.
45+
"""
346
struct DefaultWalk <: AbstractWalk end
447

548
function (::DefaultWalk)(recurse, x, ys...)
@@ -8,10 +51,27 @@ function (::DefaultWalk)(recurse, x, ys...)
851
re(map(recurse, func, yfuncs...))
952
end
1053

54+
"""
55+
StructuralWalk()
56+
57+
A structural variant of [`Functors.DefaultWalk`](@ref).
58+
The recursion behavior is identical, but the mapped children are not restructured.
59+
60+
See [`fmapstructure`](@ref) for more information.
61+
"""
1162
struct StructuralWalk <: AbstractWalk end
1263

1364
(::StructuralWalk)(recurse, x) = map(recurse, children(x))
1465

66+
"""
67+
ExcludeWalk(walk, fn, exclude)
68+
69+
A walk that recurses nodes `(x, ys...)` according to `walk`,
70+
except when `exclude(x)` is true.
71+
Then, `fn(x, ys...)` is applied instead of recursing further.
72+
73+
Typically wraps an existing `walk` for use with [`fmap`](@ref).
74+
"""
1575
struct ExcludeWalk{T, F, G} <: AbstractWalk
1676
walk::T
1777
fn::F
@@ -23,6 +83,18 @@ end
2383

2484
struct NoKeyword end
2585

86+
"""
87+
CachedWalk(walk[; prune])
88+
89+
A walk that recurses nodes `(x, ys...)` according to `walk` and storing the
90+
output of the recursion in a cache indexed by `x` (based on object ID).
91+
Whenever the cache already contains `x`, either:
92+
- `prune` is specified, then it is returned, or
93+
- `prune` is unspecified, and the previously cached recursion of `(x, ys...)`
94+
returned.
95+
96+
Typically wraps an existing `walk` for use with [`fmap`](@ref).
97+
"""
2698
struct CachedWalk{T, S} <: AbstractWalk
2799
walk::T
28100
prune::S
@@ -40,6 +112,15 @@ function (walk::CachedWalk)(recurse, x, ys...)
40112
end
41113
end
42114

115+
"""
116+
CollectWalk()
117+
118+
A walk that recurses into a node `x` via [`Functors.children`](@ref),
119+
storing the recursion history in a cache.
120+
The resulting ordered recursion history is returned.
121+
122+
See [`fcollect`](@ref) for more information.
123+
"""
43124
struct CollectWalk <: AbstractWalk
44125
cache::Base.IdSet{Any}
45126
output::Vector{Any}

0 commit comments

Comments
 (0)