# API

`Functors.AbstractWalk`

`Functors.AnonymousWalk`

`Functors.CachedWalk`

`Functors.CollectWalk`

`Functors.DefaultWalk`

`Functors.ExcludeWalk`

`Functors.IterateWalk`

`Functors.KeyPath`

`Functors.StructuralWalk`

`Functors.children`

`Functors.execute`

`Functors.fcollect`

`Functors.fleaves`

`Functors.fmap`

`Functors.fmap_with_path`

`Functors.fmapstructure`

`Functors.fmapstructure_with_path`

`Functors.functor`

`Functors.getkeypath`

`Functors.haskeypath`

`Functors.isleaf`

`Functors.@functor`

`Functors.@leaf`

## Constructors and helpers

`Functors.@functor`

— Macro```
@functor T
@functor T (x,)
```

Adds methods to `functor`

allowing recursion into objects of type `T`

, and reconstruction. Assumes that `T`

has a constructor accepting all of its fields, which is true unless you have provided an inner constructor which does not.

By default all fields of `T`

are considered `children`

; this can be restricted be restructed by providing a tuple of field names.

**Examples**

```
julia> struct Foo; x; y; end
julia> @functor Foo
julia> Functors.children(Foo(1,2))
(x = 1, y = 2)
julia> _, re = Functors.functor(Foo(1,2));
julia> re((10, 20))
Foo(10, 20)
julia> struct TwoThirds a; b; c; end
julia> @functor TwoThirds (a, c)
julia> ch2, re3 = Functors.functor(TwoThirds(10,20,30));
julia> ch2
(a = 10, c = 30)
julia> re3(("ten", "thirty"))
TwoThirds("ten", 20, "thirty")
julia> fmap(x -> 10x, TwoThirds(Foo(1,2), Foo(3,4), 56))
TwoThirds(Foo(10, 20), Foo(3, 4), 560)
```

`Functors.@leaf`

— Macro`@leaf T`

Define `functor`

for the type `T`

so that `isleaf(x::T) == true`

.

`Functors.functor`

— Function`Functors.functor(x) = functor(typeof(x), x)`

Returns a tuple containing, first, a `NamedTuple`

of the children of `x`

(typically its fields), and second, a reconstruction funciton. This controls the behaviour of `fmap`

.

Methods should be added to `functor(::Type{T}, x)`

for custom types, usually using the macro `@functor`

.

`Functors.children`

— Function`Functors.children(x)`

Return the children of `x`

as defined by `functor`

. Equivalent to `functor(x)[1]`

.

`Functors.isleaf`

— Function`Functors.fcollect`

— Function`fcollect(x; exclude = v -> false)`

Traverse `x`

by recursing each child of `x`

as defined by `functor`

and collecting the results into a flat array, ordered by a breadth-first traversal of `x`

, respecting the iteration order of `children`

calls.

Doesn't recurse inside branches rooted at nodes `v`

for which `exclude(v) == true`

. In such cases, the root `v`

is also excluded from the result. By default, `exclude`

always yields `false`

.

See also `children`

.

**Examples**

```
julia> struct Foo; x; y; end
julia> @functor Foo
julia> struct Bar; x; end
julia> @functor Bar
julia> struct TypeWithNoChildren; x; y; end
julia> m = Foo(Bar([1,2,3]), TypeWithNoChildren(:a, :b))
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
julia> fcollect(m)
4-element Vector{Any}:
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
Bar([1, 2, 3])
[1, 2, 3]
TypeWithNoChildren(:a, :b)
julia> fcollect(m, exclude = v -> v isa Bar)
2-element Vector{Any}:
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
TypeWithNoChildren(:a, :b)
julia> fcollect(m, exclude = v -> Functors.isleaf(v))
2-element Vector{Any}:
Foo(Bar([1, 2, 3]), TypeWithNoChildren(:a, :b))
Bar([1, 2, 3])
```

`Functors.fleaves`

— Function`fleaves(x; exclude = isleaf)`

Traverse `x`

by recursing each child of `x`

as defined by `functor`

and collecting the leaves into a flat array, ordered by a breadth-first traversal of `x`

, respecting the iteration order of `children`

calls.

The `exclude`

function is used to determine whether to recurse into a node, therefore identifying the leaves as the nodes for which `exclude`

returns `true`

.

See also `fcollect`

for a similar function that collects all nodes instead.

**Examples**

```
julia> struct Bar; x; end
julia> @functor Bar
julia> struct TypeWithNoChildren; x; y; end
julia> m = (a = Bar([1,2,3]), b = TypeWithNoChildren(4, 5));
julia> fleaves(m)
2-element Vector{Any}:
[1, 2, 3]
TypeWithNoChildren(4, 5)
```

## Maps

`Functors.fmap`

— Function`fmap(f, x, ys...; exclude = Functors.isleaf, walk = Functors.DefaultWalk(), [prune])`

A structure and type preserving `map`

.

By default it transforms every leaf node (identified by `exclude`

, default `isleaf`

) by applying `f`

, and otherwise traverses `x`

recursively using `functor`

. Optionally, it may also be associated with objects `ys`

with the same tree structure. In that case, `f`

is applied to the corresponding leaf nodes in `x`

and `ys`

.

See also `fmap_with_path`

and `fmapstructure`

.

**Examples**

```
julia> fmap(string, (x=1, y=(2, 3)))
(x = "1", y = ("2", "3"))
julia> nt = (a = [1,2], b = [23, (45,), (x=6//7, y=())], c = [8,9]);
julia> fmap(println, nt)
[1, 2]
23
45
6//7
()
[8, 9]
(a = nothing, b = Any[nothing, (nothing,), (x = nothing, y = nothing)], c = nothing)
julia> fmap(println, nt; exclude = x -> x isa Array)
[1, 2]
Any[23, (45,), (x = 6//7, y = ())]
[8, 9]
(a = nothing, b = nothing, c = nothing)
julia> twice = [1, 2]; # println only acts once on this
julia> fmap(println, (i = twice, ii = 34, iii = [5, 6], iv = (twice, 34), v = 34.0))
[1, 2]
34
[5, 6]
34
34.0
(i = nothing, ii = nothing, iii = nothing, iv = (nothing, nothing), v = nothing)
julia> d1 = Dict("x" => [1,2], "y" => 3);
julia> d2 = Dict("x" => [4,5], "y" => 6, "z" => "an_extra_value");
julia> fmap(+, d1, d2) == Dict("x" => [5, 7], "y" => 9) # Note that "z" is ignored
true
```

Mutable objects which appear more than once are only handled once (by caching `f(x)`

in an `IdDict`

). Thus the relationship `x.i === x.iv[1]`

will be preserved. An immutable object which appears twice is not stored in the cache, thus `f(34)`

will be called twice, and the results will agree only if `f`

is pure.

By default, `Tuple`

s, `NamedTuple`

s, and some other container-like types in Base have children to recurse into. Arrays of numbers do not. To enable recursion into new types, you must provide a method of `functor`

, which can be done using the macro `@functor`

:

```
julia> struct Foo; x; y; end
julia> @functor Foo
julia> struct Bar; x; end
julia> @functor Bar
julia> m = Foo(Bar([1,2,3]), (4, 5, Bar(Foo(6, 7))));
julia> fmap(x -> 10x, m)
Foo(Bar([10, 20, 30]), (40, 50, Bar(Foo(60, 70))))
julia> fmap(string, m)
Foo(Bar("[1, 2, 3]"), ("4", "5", Bar(Foo("6", "7"))))
julia> fmap(string, m, exclude = v -> v isa Bar)
Foo("Bar([1, 2, 3])", (4, 5, "Bar(Foo(6, 7))"))
```

To recurse into custom types without reconstructing them afterwards, use `fmapstructure`

.

For advanced customization of the traversal behaviour, pass a custom `walk`

function that subtypes `Functors.AbstractWalk`

. The call `fmap(f, x, ys...; walk = mywalk)`

will wrap `mywalk`

in `ExcludeWalk`

then `CachedWalk`

. Here, `ExcludeWalk`

is responsible for applying `f`

at excluded nodes. For a low-level interface for executing a user-constructed walk, see `execute`

.

```
julia> struct MyWalk <: Functors.AbstractWalk end
julia> (::MyWalk)(recurse, x) = x isa Bar ? "hello" :
Functors.DefaultWalk()(recurse, x)
julia> fmap(x -> 10x, m; walk = MyWalk())
Foo("hello", (40, 50, "hello"))
```

The behaviour when the same node appears twice can be altered by giving a value to the `prune`

keyword, which is then used in place of all but the first:

```
julia> twice = [1, 2];
julia> fmap(float, (x = twice, y = [1,2], z = twice); prune = missing)
(x = [1.0, 2.0], y = [1.0, 2.0], z = missing)
```

`Functors.fmap_with_path`

— Function`fmap_with_path(f, x, ys...; exclude = isleaf, walk = DefaultWalkWithPath(), [prune])`

Like `fmap`

, but also passes a `KeyPath`

to `f`

for each node in the recursion. The `KeyPath`

is a tuple of the indices used to reach the current node from the root of the recursion. The `KeyPath`

is constructed by the `walk`

function, and can be used to reconstruct the path to the current node from the root of the recursion.

`f`

has to accept two arguments: the associated `KeyPath`

and the value of the current node.

`exclude`

also receives the `KeyPath`

as its first argument and a node as its second. It should return `true`

if the recursion should not continue on its children and `f`

applied to it.

`prune`

is used to control the behaviour when the same node appears twice, see `fmap`

for more information.

**Examples**

```
julia> x = ([1, 2, 3], 4, (a=5, b=Dict("A"=>6, "B"=>7), c=Dict("C"=>8, "D"=>9)));
julia> exclude(kp, x) = kp == KeyPath(3, :c) || Functors.isleaf(x);
julia> fmap_with_path((kp, x) -> x isa Dict ? nothing : x.^2, x; exclude = exclude)
([1, 4, 9], 16, (a = 25, b = Dict("B" => 49, "A" => 36), c = nothing))
```

`Functors.fmapstructure`

— Function`fmapstructure(f, x, ys...; exclude = isleaf, [prune])`

Like `fmap`

, but doesn't preserve the type of custom structs. Instead, it returns a `NamedTuple`

(or a `Tuple`

, or an array), or a nested set of these.

Useful for when the output must not contain custom structs.

See also `fmap`

and `fmapstructure_with_path`

.

**Examples**

```
julia> struct Foo; x; y; end
julia> @functor Foo
julia> m = Foo([1,2,3], [4, (5, 6), Foo(7, 8)]);
julia> fmapstructure(x -> 2x, m)
(x = [2, 4, 6], y = Any[8, (10, 12), (x = 14, y = 16)])
julia> fmapstructure(println, m)
[1, 2, 3]
4
5
6
7
8
(x = nothing, y = Any[nothing, (nothing, nothing), (x = nothing, y = nothing)])
```

`Functors.fmapstructure_with_path`

— Function`fmapstructure_with_path(f, x, ys...; [exclude, prune])`

Like `fmap_with_path`

, but doesn't preserve the type of custom structs. Instead, it returns a named tuple, a tuple, an array, a dict, or a nested set of these.

See also `fmapstructure`

.

## Walks

`Functors.AbstractWalk`

— Type`AbstractWalk`

Any walk for use with `fmap`

should inherit from this type. A walk subtyping `AbstractWalk`

must satisfy the walk function interface:

```
struct MyWalk <: AbstractWalk end
function (::MyWalk)(recurse, x, ys...)
# implement this
end
```

The walk function is called on a node `x`

in a Functors tree. It may also be passed associated nodes `ys...`

in other Functors trees. The walk function recurses further into `(x, ys...)`

by calling `recurse`

on the child nodes. The choice of which nodes to recurse and in what order is custom to the walk.

`Functors.execute`

— Function`execute(walk, x, ys...)`

Execute a `walk`

that recursively calls itself, starting at a node `x`

in a Functors tree, as well as optional associated nodes `ys...`

in other Functors trees. Any custom `walk`

function that subtypes `Functors.AbstractWalk`

is permitted.

`Functors.DefaultWalk`

— Type`DefaultWalk()`

The default walk behavior for Functors.jl. Walks all the `Functors.children`

of trees `(x, ys...)`

based on the structure of `x`

. The resulting mapped child nodes are restructured into the type of `x`

.

See `fmap`

for more information.

`Functors.StructuralWalk`

— Type`StructuralWalk()`

A structural variant of `Functors.DefaultWalk`

. The recursion behavior is identical, but the mapped children are not restructured.

See `fmapstructure`

for more information.

`Functors.ExcludeWalk`

— Type`ExcludeWalk(walk, fn, exclude)`

A walk that recurses nodes `(x, ys...)`

according to `walk`

, except when `exclude(x)`

is true. Then, `fn(x, ys...)`

is applied instead of recursing further.

Typically wraps an existing `walk`

for use with `fmap`

.

`Functors.CachedWalk`

— Type`CachedWalk(walk[; prune])`

A walk that recurses nodes `(x, ys...)`

according to `walk`

and storing the output of the recursion in a cache indexed by `x`

(based on object ID). Whenever the cache already contains `x`

, either:

`prune`

is specified, then it is returned, or`prune`

is unspecified, and the previously cached recursion of`(x, ys...)`

returned.

Typically wraps an existing `walk`

for use with `fmap`

.

`Functors.CollectWalk`

— Type`CollectWalk()`

A walk that recurses into a node `x`

via `Functors.children`

, storing the recursion history in a cache. The resulting ordered recursion history is returned.

See `fcollect`

for more information.

`Functors.AnonymousWalk`

— Type`AnonymousWalk(walk_fn)`

Wrap a `walk_fn`

so that `AnonymousWalk(walk_fn) isa AbstractWalk`

. This type only exists for backwards compatability and should not be directly used. Attempting to wrap an existing `AbstractWalk`

is a no-op (i.e. it is not wrapped).

`Functors.IterateWalk`

— Type`IterateWalk()`

A walk that walks all the `Functors.children`

of trees `(x, ys...)`

and concatenates the iterators of the children via `Iterators.flatten`

. The resulting iterator is returned.

When used with `fmap`

, the provided function `f`

should return an iterator. For example, to iterate through the square of every scalar value:

```
julia> x = ([1, 2, 3], 4, (5, 6, [7, 8]));
julia> make_iterator(x) = x isa AbstractVector ? x.^2 : (x^2,);
julia> iter = fmap(make_iterator, x; walk=Functors.IterateWalk(), cache=nothing);
julia> collect(iter)
8-element Vector{Int64}:
1
4
9
16
25
36
49
64
```

We can also simultaneously iterate through multiple functors:

```
julia> y = ([8, 7, 6], 5, (4, 3, [2, 1]));
julia> make_zipped_iterator(x, y) = zip(make_iterator(x), make_iterator(y));
julia> zipped_iter = fmap(make_zipped_iterator, x, y; walk=Functors.IterateWalk(), cache=nothing);
julia> collect(zipped_iter)
8-element Vector{Tuple{Int64, Int64}}:
(1, 64)
(4, 49)
(9, 36)
(16, 25)
(25, 16)
(36, 9)
(49, 4)
(64, 1)
```

## KeyPath

`Functors.KeyPath`

— Type`KeyPath(keys...)`

A type for representing a path of keys to a value in a nested structure. Can be constructed with a sequence of keys, or by concatenating other `KeyPath`

s. Keys can be of type `Symbol`

, `String`

, or `Int`

.

For custom types, access through symbol keys is assumed to be done with `getproperty`

. For consistency, the method `Base.propertynames`

is used to get the viable property names.

For string and integer keys instead, the access is done with `getindex`

.

See also `getkeypath`

, `haskeypath`

.

**Examples**

```
julia> kp = KeyPath(:b, 3)
KeyPath(:b, 3)
julia> KeyPath(:a, kp, :c, 4) # construct mixing keys and keypaths
KeyPath(:a, :b, 3, :c, 4)
julia> struct T
a
b
end
julia> function Base.getproperty(x::T, k::Symbol)
if k in fieldnames(T)
return getfield(x, k)
elseif k === :ab
return "ab"
else
error()
end
end;
julia> Base.propertynames(::T) = (:a, :b, :ab);
julia> x = T(3, Dict(:c => 4, :d => 5));
julia> getkeypath(x, KeyPath(:ab)) # equivalent to x.ab
"ab"
julia> getkeypath(x, KeyPath(:b, :c)) # equivalent to (x.b)[:c]
4
```

`Functors.haskeypath`

— Function`haskeypath(x, kp::KeyPath)`

Return `true`

if `x`

has a value at the path `kp`

.

See also `KeyPath`

and `getkeypath`

.

**Examples**

```
julia> x = Dict(:a => 3, :b => Dict(:c => 4, "d" => [5, 6, 7]))
Dict{Any,Any} with 2 entries:
:a => 3
:b => Dict{Any,Any}(:c=>4,"d"=>[5, 6, 7])
julia> haskeypath(x, KeyPath(:a))
true
julia> haskeypath(x, KeyPath(:b, "d", 1))
true
julia> haskeypath(x, KeyPath(:b, "d", 4))
false
```

`Functors.getkeypath`

— Function`getkeypath(x, kp::KeyPath)`

Return the value in `x`

at the path `kp`

.

See also `KeyPath`

and `haskeypath`

.

**Examples**

```
julia> x = Dict(:a => 3, :b => Dict(:c => 4, "d" => [5, 6, 7]))
Dict{Symbol, Any} with 2 entries:
:a => 3
:b => Dict{Any, Any}(:c=>4, "d"=>[5, 6, 7])
julia> getkeypath(x, KeyPath(:b, "d", 2))
6
```