Skip to content

Commit 4a834ce

Browse files
authored
Merge pull request #34 from mcabbott/movedocs
Move the docstrings
2 parents 937c8d2 + 685a17a commit 4a834ce

File tree

2 files changed

+323
-289
lines changed

2 files changed

+323
-289
lines changed

src/Functors.jl

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,272 @@ export @functor, @flexiblefunctor, fmap, fmapstructure, fcollect
55
include("functor.jl")
66
include("base.jl")
77

8+
9+
###
10+
### Docstrings for basic functionality
11+
###
12+
13+
14+
"""
15+
Functors.functor(x) = functor(typeof(x), x)
16+
17+
Returns a tuple containing, first, a `NamedTuple` of the children of `x`
18+
(typically its fields), and second, a reconstruction funciton.
19+
This controls the behaviour of [`fmap`](@ref).
20+
21+
Methods should be added to `functor(::Type{T}, x)` for custom types,
22+
usually using the macro [@functor](@ref).
23+
"""
24+
functor
25+
26+
"""
27+
@functor T
28+
@functor T (x,)
29+
30+
Adds methods to [`functor`](@ref) allowing recursion into objects of type `T`,
31+
and reconstruction. Assumes that `T` has a constructor accepting all of its fields,
32+
which is true unless you have provided an inner constructor which does not.
33+
34+
By default all fields of `T` are considered [children](@ref);
35+
this can be restricted be restructed by providing a tuple of field names.
36+
37+
# Examples
38+
```jldoctest
39+
julia> struct Foo; x; y; end
40+
41+
julia> @functor Foo
42+
43+
julia> Functors.children(Foo(1,2))
44+
(x = 1, y = 2)
45+
46+
julia> _, re = Functors.functor(Foo(1,2));
47+
48+
julia> re((10, 20))
49+
Foo(10, 20)
50+
51+
julia> struct TwoThirds a; b; c; end
52+
53+
julia> @functor TwoThirds (a, c)
54+
55+
julia> ch2, re3 = Functors.functor(TwoThirds(10,20,30));
56+
57+
julia> ch2
58+
(a = 10, c = 30)
59+
60+
julia> re3(("ten", "thirty"))
61+
TwoThirds("ten", 20, "thirty")
62+
63+
julia> fmap(x -> 10x, TwoThirds(Foo(1,2), Foo(3,4), 56))
64+
TwoThirds(Foo(10, 20), Foo(3, 4), 560)
65+
```
66+
"""
67+
var"@functor"
68+
69+
"""
70+
Functors.isleaf(x)
71+
72+
Return true if `x` has no [`children`](@ref) according to [`functor`](@ref).
73+
74+
# Examples
75+
```jldoctest
76+
julia> Functors.isleaf(1)
77+
true
78+
79+
julia> Functors.isleaf([2, 3, 4])
80+
true
81+
82+
julia> Functors.isleaf(["five", [6, 7]])
83+
false
84+
85+
julia> Functors.isleaf([])
86+
false
87+
88+
julia> Functors.isleaf((8, 9))
89+
false
90+
91+
julia> Functors.isleaf(())
92+
true
93+
```
94+
"""
95+
isleaf
96+
97+
"""
98+
Functors.children(x)
99+
100+
Return the children of `x` as defined by [`functor`](@ref).
101+
Equivalent to `functor(x)[1]`.
102+
"""
103+
children
104+
105+
"""
106+
fmap(f, x; exclude = Functors.isleaf, walk = Functors._default_walk)
107+
108+
A structure and type preserving `map`.
109+
110+
By default it transforms every leaf node (identified by `exclude`, default [`isleaf`](@ref))
111+
by applying `f`, and otherwise traverses `x` recursively using [`functor`](@ref).
112+
113+
# Examples
114+
```jldoctest
115+
julia> fmap(string, (x=1, y=(2, 3)))
116+
(x = "1", y = ("2", "3"))
117+
118+
julia> nt = (a = [1,2], b = [23, (45,), (x=6//7, y=())], c = [8,9]);
119+
120+
julia> fmap(println, nt)
121+
[1, 2]
122+
23
123+
45
124+
6//7
125+
()
126+
[8, 9]
127+
(a = nothing, b = Any[nothing, (nothing,), (x = nothing, y = nothing)], c = nothing)
128+
129+
julia> fmap(println, nt; exclude = x -> x isa Array)
130+
[1, 2]
131+
Any[23, (45,), (x = 6//7, y = ())]
132+
[8, 9]
133+
(a = nothing, b = nothing, c = nothing)
134+
135+
julia> twice = [1, 2];
136+
137+
julia> fmap(println, (i = twice, ii = 34, iii = [5, 6], iv = (twice, 34), v = 34.0))
138+
[1, 2]
139+
34
140+
[5, 6]
141+
34.0
142+
(i = nothing, ii = nothing, iii = nothing, iv = (nothing, nothing), v = nothing)
143+
```
144+
145+
If the same node (same according to `===`) appears more than once,
146+
it will only be handled once, and only be transformed once with `f`.
147+
Thus the result will also have this relationship.
148+
149+
By default, `Tuple`s, `NamedTuple`s, and some other container-like types in Base have
150+
children to recurse into. Arrays of numbers do not.
151+
To enable recursion into new types, you must provide a method of [`functor`](@ref),
152+
which can be done using the macro [`@functor`](@ref):
153+
154+
```jldoctest withfoo
155+
julia> struct Foo; x; y; end
156+
157+
julia> @functor Foo
158+
159+
julia> struct Bar; x; end
160+
161+
julia> @functor Bar
162+
163+
julia> m = Foo(Bar([1,2,3]), (4, 5, Bar(Foo(6, 7))));
164+
165+
julia> fmap(x -> 10x, m)
166+
Foo(Bar([10, 20, 30]), (40, 50, Bar(Foo(60, 70))))
167+
168+
julia> fmap(string, m)
169+
Foo(Bar("[1, 2, 3]"), ("4", "5", Bar(Foo("6", "7"))))
170+
171+
julia> fmap(string, m, exclude = v -> v isa Bar)
172+
Foo("Bar([1, 2, 3])", (4, 5, "Bar(Foo(6, 7))"))
173+
```
174+
175+
To recurse into custom types without reconstructing them afterwards,
176+
use [`fmapstructure`](@ref).
177+
178+
For advanced customization of the traversal behaviour, pass a custom `walk` function of the form `(f', xs) -> ...`.
179+
This function walks (maps) over `xs` calling the continuation `f'` to continue traversal.
180+
181+
```jldoctest withfoo
182+
julia> fmap(x -> 10x, m, walk=(f, x) -> x isa Bar ? x : Functors._default_walk(f, x))
183+
Foo(Bar([1, 2, 3]), (40, 50, Bar(Foo(6, 7))))
184+
```
185+
"""
186+
fmap
187+
188+
189+
###
190+
### Extras
191+
###
192+
193+
194+
"""
195+
fmapstructure(f, x; exclude = isleaf)
196+
197+
Like [`fmap`](@ref), but doesn't preserve the type of custom structs.
198+
Instead, it returns a `NamedTuple` (or a `Tuple`, or an array),
199+
or a nested set of these.
200+
201+
Useful for when the output must not contain custom structs.
202+
203+
# Examples
204+
```jldoctest
205+
julia> struct Foo; x; y; end
206+
207+
julia> @functor Foo
208+
209+
julia> m = Foo([1,2,3], [4, (5, 6), Foo(7, 8)]);
210+
211+
julia> fmapstructure(x -> 2x, m)
212+
(x = [2, 4, 6], y = Any[8, (10, 12), (x = 14, y = 16)])
213+
214+
julia> fmapstructure(println, m)
215+
[1, 2, 3]
216+
4
217+
5
218+
6
219+
7
220+
8
221+
(x = nothing, y = Any[nothing, (nothing, nothing), (x = nothing, y = nothing)])
222+
```
223+
"""
224+
fmapstructure
225+
226+
"""
227+
fcollect(x; exclude = v -> false)
228+
229+
Traverse `x` by recursing each child of `x` as defined by [`functor`](@ref)
230+
and collecting the results into a flat array, ordered by a breadth-first
231+
traversal of `x`, respecting the iteration order of `children` calls.
232+
233+
Doesn't recurse inside branches rooted at nodes `v`
234+
for which `exclude(v) == true`.
235+
In such cases, the root `v` is also excluded from the result.
236+
By default, `exclude` always yields `false`.
237+
238+
See also [`children`](@ref).
239+
240+
# Examples
241+
242+
```jldoctest
243+
julia> struct Foo; x; y; end
244+
245+
julia> @functor Foo
246+
247+
julia> struct Bar; x; end
248+
249+
julia> @functor Bar
250+
251+
julia> struct NoChildren; x; y; end
252+
253+
julia> m = Foo(Bar([1,2,3]), NoChildren(:a, :b))
254+
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
255+
256+
julia> fcollect(m)
257+
4-element Vector{Any}:
258+
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
259+
Bar([1, 2, 3])
260+
[1, 2, 3]
261+
NoChildren(:a, :b)
262+
263+
julia> fcollect(m, exclude = v -> v isa Bar)
264+
2-element Vector{Any}:
265+
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
266+
NoChildren(:a, :b)
267+
268+
julia> fcollect(m, exclude = v -> Functors.isleaf(v))
269+
2-element Vector{Any}:
270+
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
271+
Bar([1, 2, 3])
272+
```
273+
"""
274+
fcollect
275+
8276
end # module

0 commit comments

Comments
 (0)