Skip to content

Commit 007076e

Browse files
committed
embiggen docstrings
1 parent 40c547c commit 007076e

File tree

4 files changed

+170
-22
lines changed

4 files changed

+170
-22
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ version = "0.2.7"
55

66
[compat]
77
julia = "1"
8+
Documenter = "0.27"
89

910
[extras]
11+
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1012
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1113
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
1214

1315
[targets]
14-
test = ["Test", "Zygote"]
16+
test = ["Test", "Documenter", "Zygote"]

docs/src/api.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
```@docs
2-
Functors.@functor
32
Functors.fmap
3+
Functors.@functor
4+
```
5+
6+
```@docs
7+
Functors.functor
48
Functors.children
59
Functors.isleaf
6-
Functors.fcollect
10+
```
11+
12+
```@docs
713
Functors.fmapstructure
14+
Functors.fcollect
815
```

src/functor.jl

Lines changed: 147 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
"""
2+
Functors.functor(x) = functor(typeof(x), x)
3+
4+
Returns a tuple containing, first, a `NamedTuple` of the children of `x`
5+
(typically its fields), and second, a reconstruction funciton.
6+
This controls the behaviour of [`fmap`](@ref).
7+
8+
Methods should be added to `functor(::Type{T}, x)` for custom types,
9+
usually using the macro [@functor](@ref).
10+
"""
111
functor(T, x) = (), _ -> x
212
functor(x) = functor(typeof(x), x)
313

@@ -29,6 +39,47 @@ function functorm(T, fs = nothing)
2939
:(makefunctor(@__MODULE__, $(esc(T)), $(fs...)))
3040
end
3141

42+
"""
43+
@functor T
44+
@functor T (x,)
45+
46+
Adds methods to [`functor`](@ref) allowing recursion into objects of type `T`,
47+
and reconstruction. Assumes that `T` has a constructor accepting all of its fields,
48+
which is true unless you have provided an inner constructor which does not.
49+
50+
By default all fields of `T` are considered [children](@ref);
51+
this can be restricted be restructed by providing a tuple of field names.
52+
53+
# Examples
54+
```jldoctest
55+
julia> struct Foo; x; y; end
56+
57+
julia> @functor Foo
58+
59+
julia> Functors.children(Foo(1,2))
60+
(x = 1, y = 2)
61+
62+
julia> _, re = Functors.functor(Foo(1,2));
63+
64+
julia> re((10, 20))
65+
Foo(10, 20)
66+
67+
julia> struct TwoThirds a; b; c; end
68+
69+
julia> @functor TwoThirds (a, c)
70+
71+
julia> ch2, re3 = Functors.functor(TwoThirds(10,20,30));
72+
73+
julia> ch2
74+
(a = 10, c = 30)
75+
76+
julia> re3(("ten", "thirty"))
77+
TwoThirds("ten", 20, "thirty")
78+
79+
julia> fmap(x -> 10x, TwoThirds(Foo(1,2), Foo(3,4), 56))
80+
TwoThirds(Foo(10, 20), Foo(3, 4), 560)
81+
```
82+
"""
3283
macro functor(args...)
3384
functorm(args...)
3485
end
@@ -61,14 +112,35 @@ macro flexiblefunctor(args...)
61112
end
62113

63114
"""
64-
isleaf(x)
115+
Functors.isleaf(x)
65116
66117
Return true if `x` has no [`children`](@ref) according to [`functor`](@ref).
118+
119+
# Examples
120+
```jldoctest
121+
julia> Functors.isleaf(1)
122+
true
123+
124+
julia> Functors.isleaf([2, 3, 4])
125+
true
126+
127+
julia> Functors.isleaf(["five", [6, 7]])
128+
false
129+
130+
julia> Functors.isleaf([])
131+
false
132+
133+
julia> Functors.isleaf((8, 9))
134+
false
135+
136+
julia> Functors.isleaf(())
137+
true
138+
```
67139
"""
68140
isleaf(x) = children(x) === ()
69141

70142
"""
71-
children(x)
143+
Functors.children(x)
72144
73145
Return the children of `x` as defined by [`functor`](@ref).
74146
Equivalent to `functor(x)[1]`.
@@ -100,18 +172,55 @@ end
100172
_default_walk(f, ::Nothing, ::Nothing) = nothing
101173

102174
"""
103-
fmap(f, x; exclude = isleaf, walk = Functors._default_walk)
104-
105-
A structure and type preserving `map` that works for all [`functor`](@ref)s.
175+
fmap(f, x; exclude = Functors.isleaf, walk = Functors._default_walk)
106176
107-
By default, traverses `x` recursively using [`functor`](@ref)
108-
and transforms every leaf node identified by `exclude` with `f`.
177+
A structure and type preserving `map`.
109178
110-
For advanced customization of the traversal behaviour, pass a custom `walk` function of the form `(f', xs) -> ...`.
111-
This function walks (maps) over `xs` calling the continuation `f'` to continue traversal.
179+
By default it transforms every leaf node (identified by `exclude`, default [`isleaf`](@ref))
180+
by applying `f`, and otherwise traverses `x` recursively using [`functor`](@ref).
112181
113182
# Examples
114183
```jldoctest
184+
julia> fmap(string, (x=1, y=(2, 3)))
185+
(x = "1", y = ("2", "3"))
186+
187+
julia> nt = (a = [1,2], b = [23, (45,), (x=6//7, y=())], c = [8,9]);
188+
189+
julia> fmap(println, nt)
190+
[1, 2]
191+
23
192+
45
193+
6//7
194+
()
195+
[8, 9]
196+
(a = nothing, b = Any[nothing, (nothing,), (x = nothing, y = nothing)], c = nothing)
197+
198+
julia> fmap(println, nt; exclude = x -> x isa Array)
199+
[1, 2]
200+
Any[23, (45,), (x = 6//7, y = ())]
201+
[8, 9]
202+
(a = nothing, b = nothing, c = nothing)
203+
204+
julia> twice = [1, 2];
205+
206+
julia> fmap(println, (i = twice, ii = 34, iii = [5, 6], iv = (twice, 34), v = 34.0))
207+
[1, 2]
208+
34
209+
[5, 6]
210+
34.0
211+
(i = nothing, ii = nothing, iii = nothing, iv = (nothing, nothing), v = nothing)
212+
```
213+
214+
If the same node (same according to `===`) appears more than once,
215+
it will only be handled once, and only be transformed once with `f`.
216+
Thus the result will also have this relationship.
217+
218+
By default, `Tuple`s, `NamedTuple`s, and some other container-like types in Base have
219+
children to recurse into. Arrays of numbers do not.
220+
To enable recursion into new types, you must provide a method of [`functor`](@ref),
221+
which can be done using the macro [`@functor`](@ref):
222+
223+
```jldoctest withfoo
115224
julia> struct Foo; x; y; end
116225
117226
julia> @functor Foo
@@ -120,19 +229,27 @@ julia> struct Bar; x; end
120229
121230
julia> @functor Bar
122231
123-
julia> m = Foo(Bar([1,2,3]), (4, 5));
232+
julia> m = Foo(Bar([1,2,3]), (4, 5, Bar(Foo(6, 7))));
124233
125-
julia> fmap(x -> 2x, m)
126-
Foo(Bar([2, 4, 6]), (8, 10))
234+
julia> fmap(x -> 10x, m)
235+
Foo(Bar([10, 20, 30]), (40, 50, Bar(Foo(60, 70))))
127236
128237
julia> fmap(string, m)
129-
Foo(Bar("[1, 2, 3]"), ("4", "5"))
238+
Foo(Bar("[1, 2, 3]"), ("4", "5", Bar(Foo("6", "7"))))
130239
131240
julia> fmap(string, m, exclude = v -> v isa Bar)
132-
Foo("Bar([1, 2, 3])", (4, 5))
241+
Foo("Bar([1, 2, 3])", (4, 5, "Bar(Foo(6, 7))"))
242+
```
243+
244+
To recurse into custom types without reconstructing them afterwards,
245+
use [`fmapstructure`](@ref).
246+
247+
For advanced customization of the traversal behaviour, pass a custom `walk` function of the form `(f', xs) -> ...`.
248+
This function walks (maps) over `xs` calling the continuation `f'` to continue traversal.
133249
134-
julia> fmap(x -> 2x, m, walk=(f, x) -> x isa Bar ? x : Functors._default_walk(f, x))
135-
Foo(Bar([1, 2, 3]), (8, 10))
250+
```jldoctest withfoo
251+
julia> fmap(x -> 10x, m, walk=(f, x) -> x isa Bar ? x : Functors._default_walk(f, x))
252+
Foo(Bar([1, 2, 3]), (40, 50, Bar(Foo(6, 7))))
136253
```
137254
"""
138255
function fmap(f, x; exclude = isleaf, walk = _default_walk, cache = IdDict())
@@ -146,7 +263,9 @@ end
146263
"""
147264
fmapstructure(f, x; exclude = isleaf)
148265
149-
Like [`fmap`](@ref), but doesn't preserve the type of custom structs. Instead, it returns a (potentially nested) `NamedTuple`.
266+
Like [`fmap`](@ref), but doesn't preserve the type of custom structs.
267+
Instead, it returns a `NamedTuple` (or a `Tuple`, or an array),
268+
or a nested set of these.
150269
151270
Useful for when the output must not contain custom structs.
152271
@@ -156,10 +275,19 @@ julia> struct Foo; x; y; end
156275
157276
julia> @functor Foo
158277
159-
julia> m = Foo([1,2,3], (4, 5));
278+
julia> m = Foo([1,2,3], [4, (5, 6), Foo(7, 8)]);
160279
161280
julia> fmapstructure(x -> 2x, m)
162-
(x = [2, 4, 6], y = (8, 10))
281+
(x = [2, 4, 6], y = Any[8, (10, 12), (x = 14, y = 16)])
282+
283+
julia> fmapstructure(println, m)
284+
[1, 2, 3]
285+
4
286+
5
287+
6
288+
7
289+
8
290+
(x = nothing, y = Any[nothing, (nothing, nothing), (x = nothing, y = nothing)])
163291
```
164292
"""
165293
fmapstructure(f, x; kwargs...) = fmap(f, x; walk = (f, x) -> map(f, children(x)), kwargs...)

test/runtests.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,16 @@ using Zygote
44
@testset "Functors.jl" begin
55

66
include("basics.jl")
7+
include("base.jl")
78
include("update.jl")
9+
10+
if VERSION < v"1.6" # || VERSION > v"1.7-"
11+
@warn "skipping doctests, on Julia $VERSION"
12+
else
13+
using Documenter
14+
@testset "doctests" begin
15+
DocMeta.setdocmeta!(Functors, :DocTestSetup, :(using Functors); recursive=true)
16+
doctest(Functors, manual=true)
17+
end
18+
end
819
end

0 commit comments

Comments
 (0)