Skip to content

Commit bc51f34

Browse files
committed
simplest isbits usecache
1 parent 0e5356f commit bc51f34

File tree

3 files changed

+34
-10
lines changed

3 files changed

+34
-10
lines changed

src/Functors.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,15 @@ julia> fmap(println, (i = twice, ii = 34, iii = [5, 6], iv = (twice, 34), v = 34
138138
[1, 2]
139139
34
140140
[5, 6]
141+
34
141142
34.0
142143
(i = nothing, ii = nothing, iii = nothing, iv = (nothing, nothing), v = nothing)
143144
```
144145
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.
146+
If the same object appears more than once, it will only be handled once, and only be
147+
transformed once with `f`. Thus the result will also have this relationship.
148+
Here "same" means `===` for non-`isbits` types. The same number (e.g. `34 === 34`) at
149+
different nodes is taken to be a coincidence, and `f` applies twice.
148150
149151
By default, `Tuple`s, `NamedTuple`s, and some other container-like types in Base have
150152
children to recurse into. Arrays of numbers do not.

src/functor.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ function _default_walk(f, x)
3939
re(map(f, func))
4040
end
4141

42+
usecache(x) = !isbits(x)
43+
4244
struct NoKeyword end
4345

44-
function fmap(f, x; exclude = isleaf, walk = _default_walk, cache = IdDict(), prune = NoKeyword())
45-
haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
46-
cache[x] = exclude(x) ? f(x) : walk(x -> fmap(f, x; exclude=exclude, walk=walk, cache=cache, prune=prune), x)
46+
function fmap(f, x; exclude = isleaf, walk = _default_walk, cache = usecache(x) ? IdDict() : nothing, prune = NoKeyword())
47+
usecache(x) && haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
48+
xnew = exclude(x) ? f(x) : walk(x -> fmap(f, x; exclude=exclude, walk=walk, cache=cache, prune=prune), x)
49+
usecache(x) && setindex!(cache, xnew, x)
50+
return xnew
4751
end
4852

4953
###
@@ -70,8 +74,10 @@ end
7074
###
7175

7276
function fmap(f, x, ys...; exclude = isleaf, walk = _default_walk, cache = IdDict(), prune = NoKeyword())
73-
haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
74-
cache[x] = exclude(x) ? f(x, ys...) : walk((xy...,) -> fmap(f, xy...; exclude=exclude, walk=walk, cache=cache, prune=prune), x, ys...)
77+
usecache(x) && haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
78+
xnew = exclude(x) ? f(x, ys...) : walk((xy...,) -> fmap(f, xy...; exclude=exclude, walk=walk, cache=cache, prune=prune), x, ys...)
79+
usecache(x) && setindex!(cache, xnew, x)
80+
return xnew
7581
end
7682

7783
function _default_walk(f, x, ys...)

test/basics.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
using Functors: functor
2+
using Functors: functor, usecache
33

44
struct Foo; x; y; end
55
@functor Foo
@@ -68,7 +68,23 @@ end
6868
m3 = Foo(Foo(shared, 1:3), Foo(1:3, shared))
6969
m3p = fmapstructure(identity, m3; prune = 0)
7070
@test m3p.y.y == 0
71-
@test_broken m3p.y.x == 1:3
71+
@test m3p.y.x == 1:3
72+
73+
# All-isbits trees need not create a cache at all:
74+
@test isbits(fmap(float, (x=1, y=(2, 3), z=4:5)))
75+
@test_skip 0 == @allocated fmap(float, (x=1, y=(2, 3), z=4:5))
76+
77+
@testset "usecache" begin
78+
@test usecache([1,2])
79+
@test usecache(Ref(3))
80+
81+
@test !usecache(4.0)
82+
@test !usecache((5, 6.0))
83+
@test !usecache((a = 2pi, b = missing))
84+
85+
@test usecache(Bar([1,2]))
86+
@test !usecache(Bar((3,4)))
87+
end
7288
end
7389

7490
@testset "functor(typeof(x), y) from @functor" begin

0 commit comments

Comments
 (0)