EltypeExtensions.jl
EltypeExtensions.jl is a mini toolbox for eltype-related conversions. The motivation of this package comes from manipulating (nested) arrays with different eltypes. However if you have any reasonable idea that works on other instances, feel free to write an issue/pull request.
We note that this package has some overlap with TypeUtils.jl and Unitless.jl.
Introduction
convert_eltype and _to_eltype
convert_eltype(T, x) works like convert(T, x), except that T refers to the eltype of the result. This can be useful for generic codes.
It should be always true that convert_eltype(T, x) isa _to_eltype(T, typeof(x)). However, since convert_eltype and _to_eltype use different routines, it's possible that the equality doesn't hold for some types. Please submit an issue or PR if that happens.
If typeof(x) is not in Base or stdlib, the package who owns the type should implement corresponding _to_eltype or convert_eltype. convert_eltype has fallbacks, in which case it could be unnecessary:
- For a subtype of
AbstractArray,convert_eltypecalls the constructorAbstractArray{T}and_to_eltypereturnsArray. - For a subtype of
AbstractUnitRange,convert_eltypecalls the constructorAbstractUnitRange{T}. - For a subtype of
AbstractRange,convert_eltypeuses broadcast throughmap. - For a
Tuple,convert_eltypeuses dot broadcast. - For other types,
convert_eltypecallsconvertand_to_eltype.
However, _to_eltype must be implemented for each type to support convert_basetype and convert_precisiontype. The following types from Base and stdlib are explicitly supported by _to_eltype:
AbstractArray, AbstractDict, AbstractSet, Adjoint, Bidiagonal, BitArray, CartesianIndices, Diagonal, Dict, Hermitian, Set, StepRangeLen, Symmetric, SymTridiagonal, Transpose, TwicePrecision, UnitRangebasetype and precisiontype
The basetype is used for nested collections, where eltype is repeatedly applied until the bottom. precisiontype has a similar idea, but goes deeper when possible. precisiontype is used to manipulate the accuracy of (nested) collections.
julia> basetype(Set{Matrix{Vector{Matrix{Complex{Rational{Int}}}}}})Complex{Rational{Int64}}julia> precisiontype(Set{Matrix{Vector{Matrix{Complex{Rational{Int}}}}}})Rational{Int64}
Method naming convention
sometype(T)gets thesometypeof typeT.sometype(x) = sometype(typeof(x))is also provided for convenience._to_sometype(T,S)converts the typeSto have thesometypeofT.convert_sometype(T,A)convertsAto have thesometypeofT.
where some can be el, base and precision.
On convert_precisiontype
convert_precisiontype accepts an optional third argument prec.
- When
Thas static precision,prechas no effect. - When
Thas dynamic precision,precspecifies the precision of conversion. Whenprecis not provided, the precision is decided by the external setup fromT. The difference is significant whenconvert_precisiontypeis called by another function:julia> precision(BigFloat)256julia> f(x) = convert_precisiontype(BigFloat, x, 256)f (generic function with 1 method)julia> g(x) = convert_precisiontype(BigFloat, x)g (generic function with 1 method)julia> setprecision(128)128julia> f(π) # static precision3.141592653589793238462643383279502884197169399375105820974944592307816406286198julia> g(π) # precision varies with the global setting3.141592653589793238462643383279502884195 - When
Tis an integer, the conversion will dig intoRationalas well. In contrast, sinceRationalas a whole is more "precise" than an integer,precisiontypedoesn't unwrapRational.julia> precisiontype(convert_precisiontype(Int128, Int8(1)//Int8(2)))Rational{Int128}
Notable behaviours
Ranges
Ranges in Julia are not consistently processed:
julia> r = StepRange(1,1,5)1:1:5julia> Float64.(r) |> typeofVector{Float64} (alias for Array{Float64, 1})julia> map(Float64,r) |> typeofStepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}}
We adapt _to_eltype to the return type of Base.map:
julia> _to_eltype(Float64, StepRange{Int,Int})ERROR: UndefVarError: _to_eltype not defined