Prepended underscore in Base functions

I see that in Base there are some functions that are prepended by an underscore. For instance, in Array.jl:

_collect(cont, itr, ::HasEltype, isz::SizeUnknown)

Is this prepended underscore a convention in Julia to indicate that a function is local to a module/method/function and thus not intended to be exported?

The convention is for these functions to be called as part of a pipeline dispatch, for example,

collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr))

_collect(::Type{T}, itr, isz::HasLength) where {T} = copyto!(Vector{T}(undef, Int(length(itr)::Integer)), itr)
_collect(::Type{T}, itr, isz::HasShape) where {T}  = copyto!(similar(Array{T}, axes(itr)), itr)
function _collect(::Type{T}, itr, isz::SizeUnknown) where T
    a = Vector{T}()
    for x in itr
        push!(a,x)
    end
    return a
end

in that case, the non underscored prepended function is called first and then it calls a method which has the prepended underscored and same name.

The idea is to have a dispatch on only the necessary arguments, but use dispatch to get the desired behavior for each type/case. Because how these are used, the exported functions usually are limited to the non-underscored prepended functions as these are internal and only used in the dispatch pipeline.

3 Likes

Yes, by convention a single leading underscore refers to functions for internal use only, similar to Python.

This is one example, but there are lots of other internal functions, e.g. Base._unsetenv that are not used in this way.

In general, an prepended underscore is typically used to indicate some kind of internal implementation detail, especially functions that make special assumptions about the arguments and so aren’t safe for external use.

5 Likes