Skip to content

Commit 4564a90

Browse files
add @leaf macro (#55)
* add `@leaf` macro * move code around * fix test, add docstring * add `@leaf` to documentation * simplify macro
1 parent c88fc26 commit 4564a90

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

docs/src/api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
```@docs
22
Functors.fmap
33
Functors.@functor
4+
Functors.@leaf
45
```
56

67
```@docs

src/Functors.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,26 +282,26 @@ julia> struct Bar; x; end
282282
283283
julia> @functor Bar
284284
285-
julia> struct NoChildren; x; y; end
285+
julia> struct TypeWithNoChildren; x; y; end
286286
287-
julia> m = Foo(Bar([1,2,3]), NoChildren(:a, :b))
288-
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
287+
julia> m = Foo(Bar([1,2,3]), TypeWithNoChildren(:a, :b))
288+
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
289289
290290
julia> fcollect(m)
291291
4-element Vector{Any}:
292-
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
292+
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
293293
Bar([1, 2, 3])
294294
[1, 2, 3]
295-
NoChildren(:a, :b)
295+
TypeWithNoChildren(:a, :b)
296296
297297
julia> fcollect(m, exclude = v -> v isa Bar)
298298
2-element Vector{Any}:
299-
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
300-
NoChildren(:a, :b)
299+
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
300+
TypeWithNoChildren(:a, :b)
301301
302302
julia> fcollect(m, exclude = v -> Functors.isleaf(v))
303303
2-element Vector{Any}:
304-
Foo(Bar([1, 2, 3]), NoChildren(:a, :b))
304+
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
305305
Bar([1, 2, 3])
306306
```
307307
"""

src/functor.jl

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
function functor end
12

2-
functor(T, x) = (), _ -> x
3+
const NoChildren = Tuple{}
4+
5+
"""
6+
@leaf T
7+
8+
Define [`functor`](@ref) for the type `T` so that `isleaf(x::T) == true`.
9+
"""
10+
macro leaf(T)
11+
:($Functors.functor(::Type{<:$(esc(T))}, x) = ($Functors.NoChildren(), _ -> x))
12+
end
13+
14+
@leaf Any # every type is a leaf by default
315
functor(x) = functor(typeof(x), x)
416

517
functor(::Type{<:Tuple}, x) = x, identity
618
functor(::Type{<:NamedTuple{L}}, x) where L = NamedTuple{L}(map(s -> getproperty(x, s), L)), identity
719
functor(::Type{<:Dict}, x) = Dict(k => x[k] for k in keys(x)), identity
820

921
functor(::Type{<:AbstractArray}, x) = x, identity
10-
functor(::Type{<:AbstractArray{<:Number}}, x) = (), _ -> x
22+
@leaf AbstractArray{<:Number}
1123

1224
function makefunctor(m::Module, T, fs = fieldnames(T))
1325
yᵢ = 0
@@ -31,7 +43,7 @@ macro functor(args...)
3143
functorm(args...)
3244
end
3345

34-
isleaf(@nospecialize(x)) = children(x) === ()
46+
isleaf(@nospecialize(x)) = children(x) === NoChildren()
3547

3648
children(x) = functor(x)[1]
3749

test/basics.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct NoChild{T}; x::T; end
2424
has_children = Foo(1, 2)
2525
@test Functors.isleaf(no_children)
2626
@test !Functors.isleaf(has_children)
27-
@test Functors.children(no_children) == ()
27+
@test Functors.children(no_children) === Functors.NoChildren()
2828
@test Functors.children(has_children) == (x=1, y=2)
2929
end
3030

@@ -352,3 +352,14 @@ end
352352
@test fmap(+, m1, n1) == Dict("x" => [5, 7], "y" => Dict(:a=>3.1, :b=>4.2))
353353
end
354354
end
355+
356+
@testset "@leaf" begin
357+
struct A; x; end
358+
@functor A
359+
a = A(1)
360+
@test Functors.children(a) === (x = 1,)
361+
Functors.@leaf A
362+
children, re = Functors.functor(a)
363+
@test children == Functors.NoChildren()
364+
@test re(children) === a
365+
end

0 commit comments

Comments
 (0)