Functional implementation of collect

It seems that simply testing if a new collection was allocated with isequal generates super-efficient code, eg

# "functional" implementation take 2
store!_or_widen(collection::Vector{T}, elt::S) where {T, S <: T} = push!(collection, elt)
store!_or_widen(collection, elt) = vcat(collection, elt)

function mycollect_funeq(itr)
    y = iterate(itr)
    y ≡ nothing && error("not allowed")
    elt, state = y
    _mycollect_funeq([elt], itr, state)
end

function _mycollect_funeq(collection, itr, state)
    while true
        y = iterate(itr, state)
        y ≡ nothing && return collection
        elt, state = y
        expanded = store!_or_widen(collection, elt)
        expanded ≡ collection || return _mycollect_funeq(expanded, itr, state)
    end
end

benchmarks as

julia> @btime collect($itr);
  60.238 μs (14 allocations: 30.94 KiB)

julia> @btime mycollect_fun($itr);
  37.332 μs (1520 allocations: 70.88 KiB)

julia> @btime mycollect_mut($itr);
  41.366 μs (990 allocations: 46.30 KiB)

julia> @btime mycollect_funeq($itr); # this is the new one
  28.801 μs (520 allocations: 39.64 KiB)

1 Like