# How to make your data type reducible

Let's see how to make a vector-of-vector a reducible collection; i.e., a type that can be fed to `foldl`

.

```
struct VecOfVec{T}
vectors::Vector{Vector{T}}
end
```

## Method 1: The `__foldl__`

interface:

We need `@next`

and `complete`

to invoke the reducing function `rf`

.

```
using Transducers
using Transducers: @next, complete
```

Supporting `foldl`

and similar only requires `Transducers.__foldl__`

:

```
function Transducers.__foldl__(rf, val, vov::VecOfVec)
for vector in vov.vectors
for x in vector
val = @next(rf, val, x)
end
end
return complete(rf, val)
end
```

Note that it's often a good idea to implement `Base.eltype`

:

`Base.eltype(::VecOfVec{T}) where {T} = T`

It can be then used as the input to the transducers:

```
vov = VecOfVec(collect.([1:n for n in 1:3]))
collect(Map(identity), vov)
```

6-element Vector{Int64}: 1 1 2 1 2 3

Macro `@next`

is used instead of function `next`

to avoid the boilerplate for supporting early termination (see the details in in `@next`

documentation). In practice, using `@next`

means that your `__foldl__`

supports early termination:

`vov |> Take(3) |> collect`

3-element Vector{Int64}: 1 1 2

More complex example:

`vov |> PartitionBy(isequal(1)) |> Zip(Map(copy), Map(sum)) |> collect`

4-element Vector{Tuple{Vector{Int64}, Int64}}: ([1, 1], 2) ([2], 2) ([1], 1) ([2, 3], 5)

## Method 2: In terms of existing transducers and reducibles with `asfoldable`

Transducers.jl has a function `Transducers.asfoldable`

which can be overloaded to convert your existing type into a foldable before being fed into `__foldl__`

. This allows us to express a new type in terms of existing structures:

```
struct VecOfVec2{T}
vectors::Vector{T}
end
Transducers.asfoldable(vov::VecOfVec2) = vov.vectors |> Cat()
```

This simply tells Transducers.jl that a `VecOfVec2`

may be folded over by simply applying the `Cat`

transducer to the `.vectors`

field of the struct. This is enough to reproduce all the examples from the previous section:

```
vov2 = VecOfVec2(collect.([1:n for n in 1:3]))
collect(Map(identity), vov2)
```

6-element Vector{Int64}: 1 1 2 1 2 3

`vov2 |> Take(3) |> collect`

3-element Vector{Int64}: 1 1 2

`vov2 |> PartitionBy(isequal(1)) |> Zip(Map(copy), Map(sum)) |> collect`

4-element Vector{Tuple{Vector{Int64}, Int64}}: ([1, 1], 2) ([2], 2) ([1], 1) ([2, 3], 5)

## Comparison to defining an Iterator:

Notice that writing `Transducers.__foldl__`

is very straightforward comparing to how to define an iterator:

`function Base.iterate(vov::VecOfVec, state=(1, 1))`

Iterator `state`

is a tuple of an index `i`

to `vov.vectors`

and an index `j`

to `vov.vectors[i]`

:

` i, j = state`

If `i`

is larger than the number of items, we are done:

` i > length(vov.vectors) && return nothing`

If `j`

is in bound, we are iterating the same sub-vector:

```
vi = vov.vectors[i]
if j <= length(vi)
return vi[j], (i, j + 1)
end
```

Otherwise, find the next non-empty sub-vector and start iterating it:

```
for k in i + 1:length(vov.vectors)
vk = vov.vectors[k]
if !isempty(vk)
return vk[1], (k, 2) # i=k, j=2
end
end
return nothing
end
Base.length(vov::VecOfVec) = sum(length, vov.vectors)
collect(vov)
```

6-element Vector{Int64}: 1 1 2 1 2 3

*This page was generated using Literate.jl.*