Why is Base.OneTo not exported?

It’s quite weird (and somewhat worrying) how I can’t refer to Base.OneTo as just OneTo:

$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.5.3 (2020-11-09)
 _/ |\__'_|_|_|\__'_|  |
|__/                   |

julia> OneTo
ERROR: UndefVarError: OneTo not defined
Stacktrace:
 [1] top-level scope
 [2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.3/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

julia> Base.OneTo
Base.OneTo

julia> StepRangeLen # Another Base resident
StepRangeLen

julia> using Base

julia> OneTo
ERROR: UndefVarError: OneTo not defined
Stacktrace:
 [1] top-level scope
 [2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.3/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

Is this for backwards compatibility with user programs that may have used the name OneTo in older programs? Is Base.OneTo experimental? The documentation does not give me that impression.

There’s very little reason for it to be exported. the vast majority of the time you’re fine with 1:n rather than OneTo(n). If you ever find yourself needing OneTo, you can just do

using Base: OneTo

Base.OneTo is not new or experimental.

4 Likes

Are there other such features that are meant for the public and documented, but not exported?

I recently proposed exporting it’s lowercase cousin oneto. You may be interested in the discussion in that closed PR:
https://github.com/JuliaLang/julia/pull/39242

A related PR is returning Base.OneTo from range(; stop) or range(; length) where stop or length are given as the only keyword argument:
https://github.com/JuliaLang/julia/pull/39241

A more controversial idea is to implement range(stop::Integer) = Base.OneTo(stop) where a single positional argument is interpreted as stop. This would be analogous to range(stop) in Python except that it would start with 1 and include stop, which is to be expected due to 1-based indexing.

The main argument against making Base.OneTo accessible is that you can get almost the same result and performance from a standard UnitRange (e.g. 1:5) as @Mason pointed out and is analyzed in depth in #39242 . The one positional argument form also seems inconsistent with other positional argument forms of range.

That said I think the single keyword forms of range present a reasonable solution that is unambiguous and exposes an efficient code path in a natural way to users.

3 Likes

To actually answer this question about whether there are documented unexported symbols in Base, here is some code:

julia> unexported_symbols_in_base = setdiff(names(Base, all = true), names(Base))

julia> meta = Base.Docs.meta(Base);

julia> hasdoc(s) = haskey(meta, Base.Docs.Binding(Base,s))
hasdoc (generic function with 1 method)

julia> filter(hasdoc,unexported_symbols_in_base)
135-element Array{Symbol,1}:
 Symbol("@irrational")
 Symbol("@kwdef")
 Symbol("@locals")
 Symbol("@propagate_inbounds")
 Symbol("@pure")
 :AbstractLock
 :AlwaysLockedST
 :AsyncCollector
 :AsyncCondition
 :AsyncGenerator
 :BottomRF
 :CFunction
 :CodeUnits
 :CyclePadding
 :EnvDict
 :Event
 :FilteringRF
 :Fix1
 :Fix2
 :FlatteningRF
 :Generator
 :GenericCondition
 :IdentityUnitRange
 :ImmutableDict
 :IteratorEltype
 :IteratorSize
 :LogicalIndex
 :MappingRF
 :OneTo
...
5 Likes