Skip to content

Commit 0060dbe

Browse files
committed
Split code in several files
1 parent d848e98 commit 0060dbe

File tree

8 files changed

+1218
-1201
lines changed

8 files changed

+1218
-1201
lines changed

src/TypeUtils.jl

Lines changed: 8 additions & 1201 deletions
Large diffs are not rendered by default.

src/arrays.jl

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Methods related to arrays.
2+
3+
"""
4+
as_array_axes(args...) -> rngs::ArrayAxes
5+
6+
converts array dimensions or ranges `args...` to a canonical form of array axes, that is a
7+
tuple of `AbstractUnitRange{eltype(Dims)}`s. Any integer in `args...` is replaced by an
8+
instance of `Base.OneTo{eltype(Dims)}`.
9+
10+
The array dimensions or ranges may also be provided as a tuple.
11+
[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which
12+
`as_array_axes` is applicable.
13+
14+
Also see [`as_array_shape`](@ref), [`as_array_size`](@ref), [`as_array_axis`](@ref),
15+
[`ArrayAxes`](@ref), `Dims`, and [`new_array`](@ref).
16+
17+
"""
18+
as_array_axes(::Tuple{}) = ()
19+
as_array_axes(args::eltype(RelaxedArrayShape)...) = as_array_axes(args)
20+
as_array_axes(rngs::ArrayAxes) = rngs
21+
as_array_axes(args::RelaxedArrayShape) = map(as_array_axis, args)
22+
23+
"""
24+
as_array_axis(arg) -> rng::ArrayAxis
25+
26+
converts array dimension or range `arg` to a canonical array axis, that is an instance of
27+
`AbstractUnitRange{eltype(Dims)}`. If `arg` is an integer, `Base.OneTo{eltype(Dims)}(arg)`
28+
is returned. [`eltype(RelaxedArrayShape)`](@ref RelaxedArrayShape) is the union of types
29+
to which `as_array_axis` is applicable.
30+
31+
Also see [`as_array_axes`](@ref), [`as_array_dim`](@ref), and `Dims`.
32+
33+
"""
34+
as_array_axis(dim::Integer) = Base.OneTo{Dim}(dim)
35+
as_array_axis(rng::ArrayAxis) = rng
36+
as_array_axis(rng::AbstractUnitRange{<:Integer}) = ArrayAxis(rng)
37+
as_array_axis(rng::AbstractRange{<:Integer}) =
38+
isone(step(rng)) ? UnitRange{Dim}(first(rng), last(rng)) : throw_non_unit_step(rng)
39+
40+
"""
41+
as_array_dim(arg) -> dim::eltype(Dims)
42+
43+
converts array dimension or range `arg` to a canonical array dimension, that is an
44+
`eltype(Dims)`. If `arg` is a unit-step range, its length is returned.
45+
[`eltype(RelaxedArrayShape)`](@ref RelaxedArrayShape) is the union of types to which
46+
`as_array_dim` is applicable.
47+
48+
Also see [`as_array_size`](@ref), [`as_array_axis`](@ref), and `Dims`.
49+
50+
"""
51+
as_array_dim(dim::Dim) = dim
52+
as_array_dim(dim::Integer) = as(Dim, dim)
53+
as_array_dim(rng::AbstractUnitRange{<:Integer}) = as_array_dim(length(rng))
54+
as_array_dim(rng::AbstractRange{<:Integer}) =
55+
isone(step(rng)) ? as_array_dim(length(rng)) : throw_non_unit_step(rng)
56+
57+
@noinline throw_non_unit_step(rng::AbstractRange) = throw(ArgumentError(
58+
"invalid non-unit step ($(step(rng))) for array axis"))
59+
60+
"""
61+
as_array_shape(args...) -> r::Union{Dims,ArraysAxes}
62+
63+
converts array dimensions or ranges `args...` to a canonical form of array shape, one of:
64+
65+
* array size, that is a tuple of `Int`s. This is the result if all of `args...` are
66+
integers or instances of `Base.OneTo`, the latter, if any, being replaced by their
67+
lengths.
68+
69+
* array axes, that is a tuple of `AbstractUnitRange{Int}`s. This is the result if any of
70+
`args...` are non-`Base.OneTo` ranges, the integers being converted to instances of
71+
`Base.OneTo{eltype(Dims)}`.
72+
73+
The array dimensions or ranges may also be provided as a tuple.
74+
[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which
75+
`as_array_shape` is applicable.
76+
77+
Also see [`as_array_size`](@ref), [`as_array_axes`](@ref), [`ArrayAxes`](@ref), `Dims`,
78+
and [`new_array`](@ref).
79+
80+
"""
81+
as_array_shape(::Tuple{}) = ()
82+
as_array_shape(args::eltype(RelaxedArrayShape)...) = as_array_shape(args)
83+
as_array_shape(args::RegularArrayShape) = as_array_size(args)
84+
as_array_shape(args::RelaxedArrayShape) = as_array_axes(args)
85+
86+
"""
87+
as_array_size(args...) -> dims::Dims
88+
89+
converts array dimensions or ranges `args...` to a canonical form of array size, that is a
90+
tuple of `eltype(Dims)`s. Any range in `args...` is replaced by its length.
91+
92+
The array dimensions or ranges may also be provided as a tuple.
93+
[`RelaxedArrayShape{N}`](@ref) is the union of types of `N`-tuples to which
94+
`as_array_size` is applicable.
95+
96+
Also see [`as_array_shape`](@ref), [`as_array_axes`](@ref), [`as_array_dim`](@ref),
97+
`Dims`, and [`new_array`](@ref).
98+
99+
"""
100+
as_array_size(::Tuple{}) = ()
101+
as_array_size(args::eltype(RelaxedArrayShape)...) = as_array_size(args)
102+
as_array_size(dims::Dims) = dims
103+
as_array_size(args::RelaxedArrayShape) = map(as_array_dim, args)
104+
105+
"""
106+
new_array(T, inds...) -> A
107+
new_array(T, (inds...,)) -> A
108+
109+
create a new array with element type `T` and shape defined by `inds...`, a list of array
110+
dimension lengths and/or index ranges. The shape may also be specified as a tuple.
111+
112+
If `inds...` contains any index range other than `Base.OneTo`, an `OffsetArray{T}` is
113+
returned; otherwise an `Array{T}` is returned. In the former case, an exception is thrown
114+
if the package `OffsetArrays` has not been loaded.
115+
116+
Also see [`as_array_shape`](@ref), [`as_array_axes`](@ref), and [`as_array_size`](@ref).
117+
118+
"""
119+
new_array(::Type{T}, shape::eltype(RelaxedArrayShape)...) where {T} = new_array(T, shape)
120+
new_array(::Type{T}, shape::RelaxedArrayShape) where {T} = new_array(T, as_array_shape(shape))
121+
new_array(::Type{T}, shape::RegularArrayShape) where {T} = new_array(T, as_array_size(shape))
122+
new_array(::Type{T}, shape::Tuple{}) where {T} = Array{T}(undef)
123+
new_array(::Type{T}, shape::Dims{N}) where {T,N} = Array{T,N}(undef, shape)
124+
new_array(::Type, shape::Union{Unsupported,ArrayAxes}) =
125+
error("package `OffsetArrays` must be loaded for such array index ranges")
126+
127+
"""
128+
promote_eltype(args...)
129+
130+
yields the promoted element type of its arguments. Arguments `args...` may be
131+
anything implementing the `eltype` method.
132+
133+
"""
134+
promote_eltype() = promote_type()
135+
promote_eltype(arg) = eltype(arg)
136+
@inline promote_eltype(args...) = promote_type(map(eltype, args)...)
137+
138+
"""
139+
convert_eltype(T, A) -> B
140+
141+
converts the element type of object/type `A` to type `T`. The returned object/type is
142+
similar to `A` except maybe for the element type. For example, if `A` is a range, then `B`
143+
is also a range. If `T` is the element type of `A`, then `A` may be returned.
144+
145+
Consider using [`as_eltype(T, A)`](@ref as_eltype) to build an object that lazily performs
146+
the conversion.
147+
148+
To simplify extending `convert_eltype` for objects `A` of given type, the default behavior
149+
is:
150+
151+
convert_eltype(T, A) = as(convert_eltype(T, typeof(A)), A)
152+
153+
so that it may be sufficient to extend `convert_eltype` for the type of the objects.
154+
155+
"""
156+
convert_eltype(::Type{T}, x::X) where {T,X} = as(convert_eltype(T, X), x)
157+
convert_eltype(::Type{T}, ::Type{X}) where {T,X} =
158+
error("don't know how to convert the element type of type `$X` to `$T`")
159+
160+
convert_eltype(::Type{T}, A::AbstractArray{T}) where {T} = A
161+
convert_eltype(::Type{T}, A::AbstractArray) where {T} = as(AbstractArray{T}, A)
162+
convert_eltype(::Type{T}, ::Type{<:Array{<:Any,N}}) where {T,N} = Array{T,N}
163+
convert_eltype(::Type{T}, ::Type{<:AbstractArray{<:Any,N}}) where {T,N} = AbstractArray{T,N}
164+
165+
# Convert element type for numbers.
166+
convert_eltype(::Type{T}, ::Type{<:Number}) where {T} = T
167+
168+
# Convert element type for tuples. See `_countuple` in `base/tuple.jl` for the best
169+
# way to extract the number of elements in a tuple given its type.
170+
convert_eltype(::Type{T}, ::Type{<:NTuple{N,Any}}) where {N,T} = NTuple{N,T}
171+
convert_eltype(::Type{T}, A::NTuple{N,T}) where {N,T} = A
172+
convert_eltype(::Type{T}, A::Tuple) where {T} = map(as(T), A)
173+
174+
# Convert element type for `Base.OneTo` and `UnitRange`. For `T` non-integer,
175+
# a `Base.OneTo` instance becomes a `UnitRange` one (in a predictible way).
176+
convert_eltype(::Type{T}, ::Type{<:OneTo}) where {T<:Integer} = OneTo{T}
177+
convert_eltype(::Type{T}, ::Type{<:OneTo}) where {T} = UnitRange{T}
178+
convert_eltype(::Type{T}, ::Type{<:UnitRange}) where {T} = UnitRange{T}
179+
convert_eltype(::Type{T}, A::Union{OneTo,UnitRange}) where {T} =
180+
as(convert_eltype(T, typeof(A)), A)
181+
182+
# Convert element type for AbstractUnitRange{T} <: OrdinalRange{T,T}.
183+
convert_eltype(::Type{T}, ::Type{<:AbstractUnitRange}) where {T} = AbstractUnitRange{T}
184+
convert_eltype(::Type{T}, A::AbstractUnitRange{T}) where {T} = A
185+
convert_eltype(::Type{T}, A::AbstractUnitRange) where {T} =
186+
as(T, first(A)):as(T, last(A))
187+
188+
# Convert element type for other range types.
189+
convert_eltype(::Type{T}, ::Type{<:AbstractRange}) where {T} = AbstractRange{T}
190+
convert_eltype(::Type{T}, A::AbstractRange{T}) where {T} = A
191+
convert_eltype(::Type{T}, A::AbstractRange) where {T} =
192+
as(T, first(A)):as(T, step(A)):as(T, last(A))
193+
194+
# Convert element type for LinRange{T,L<:Integer} <: AbstractRange{T}.
195+
convert_eltype(::Type{T}, A::LinRange{T}) where {T} = A
196+
convert_eltype(::Type{T}, A::LinRange) where {T} =
197+
LinRange(as(T, first(A)), as(T, last(A)), length(A))
198+
199+
"""
200+
convert_eltype(T) -> f
201+
202+
yields a callable object `f` such that `f(x)` yields `convert_eltype(T, x)` for any `x`.
203+
204+
"""
205+
convert_eltype(::Type{T}) where {T} = Converter(convert_eltype, T)
206+
207+
"""
208+
as_eltype(T, A) -> B
209+
210+
yields an array which lazily converts its entries to type `T`. More specifically, a call
211+
like `B[i]` yields `as(T,A[i])`.
212+
213+
Consider using [`convert_eltype(T, A)`](@ref convert_eltype) to perform the conversion
214+
once and immediately.
215+
216+
"""
217+
as_eltype(::Type{T}, A::AbstractArray{T}) where {T} = A
218+
as_eltype(::Type{T}, A::AbstractArray) where {T} = AsEltype{T}(A)
219+
220+
Base.parent(A::AsEltype) = getfield(A, :parent)
221+
222+
# Implement abstract array API for `AsEltype` objects.
223+
for func in (:axes, :length, :size)
224+
@eval Base.$func(A::AsEltype) = $func(parent(A))
225+
end
226+
for (L, S, Idecl, Icall) in ((false, :IndexCartesian, :(I::Vararg{Int,N}), :(I...)),
227+
(true, :IndexLinear, :(i::Int), :(i)))
228+
@eval begin
229+
Base.IndexStyle(::Type{<:AsEltype{T,N,$L}}) where {T,N} = $S()
230+
@inline function Base.getindex(A::AsEltype{T,N,$L}, $Idecl) where {T,N}
231+
@boundscheck checkbounds(A, $Icall)
232+
r = @inbounds getindex(parent(A), $Icall)
233+
return as(T, r)
234+
end
235+
@inline function Base.setindex!(A::AsEltype{T,N,$L}, x, $Idecl) where {T,N}
236+
@boundscheck checkbounds(A, $Icall)
237+
@inbounds setindex!(parent(A), x, $Icall)
238+
return A
239+
end
240+
end
241+
end
242+
243+
Base.similar(A::AsEltype, ::Type{T}) where {T} = similar(parent(A), T)
244+
for shape in (:Dims,
245+
:(Tuple{Integer,Vararg{Integer}}),
246+
:(Tuple{Union{Integer,UnitRange{<:Integer}},
247+
Vararg{Union{Integer,UnitRange{<:Integer}}}}))
248+
@eval Base.similar(A::AsEltype, ::Type{T}, shape::$shape) where {T} =
249+
similar(parent(A), T, shape)
250+
end

src/funcs.jl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Methods related to functions.
2+
3+
"""
4+
return_type(f, argtypes...) -> T
5+
6+
yields the type of the result returned by the callable object `f` when called
7+
with arguments of types `argtypes...`.
8+
9+
See the warning in the documentation of `Base.promote_op` for the fragility of such
10+
inference in some cases. There are no such issues if `f` is an instance of
11+
[`TypeStableFunction`](@ref), e.g. built by [`as_return`][@ref), however `argtypes...` are
12+
not checked for validity for such objects.
13+
14+
"""
15+
return_type(::TypeStableFunction{T}, ::DataType...) where {T} = T
16+
return_type(::Type{<:TypeStableFunction{T}}, ::DataType...) where {T} = T
17+
return_type(f, argtypes::DataType...) = Base.promote_op(f, argtypes...)
18+
19+
"""
20+
as_return(T, f) -> g
21+
TypeStableFunction{T}(f) -> g
22+
TypeStableFunction(f, argtypes...) -> g
23+
24+
yield a callable object `g` that wraps callable `f` for guaranteed returned type `T`.
25+
Alternatively, the type(s) `argtypes...` of the function argument(s) can be specified to
26+
infer the returned type `T`. Then the following holds:
27+
28+
g(args...; kwds...) === as(T, f(args...; kwds...))
29+
30+
for any arguments `args...` and keywords `kwds...`. Note that due to limitation of the
31+
`Base.promote_op` method, it is currently not possible to infer `T` based on the types of
32+
the keywords.
33+
34+
Methods [`return_type(g)`](@ref) and `parent(g)` may be used to retrieve `T` and `f`
35+
respectively.
36+
37+
If `T` is specified, a similar object is given by:
38+
39+
g = as(T)∘f
40+
41+
"""
42+
as_return(::Type{T}, f) where {T} = TypeStableFunction{T}(f)
43+
44+
@doc @doc(as_return) TypeStableFunction
45+
46+
# Outer constructor.
47+
function TypeStableFunction(f, argtypes::DataType...)
48+
T = Base.promote_op(f, argtypes...)
49+
R = T isa Union ? promote_type(ntuple(i -> getfield(T, i), Val(nfields(T)))...) : T
50+
isconcretetype(R) || throw(ArgumentError("cannot promote `$T` to a single concrete type"))
51+
return TypeStableFunction{R}(f)
52+
end
53+
54+
# Conversion constructors.
55+
TypeStableFunction{T}(f::TypeStableFunction{T}) where {T} = f
56+
TypeStableFunction{T}(f::TypeStableFunction) where {T} = TypeStableFunction{T}(parent(f))
57+
58+
# Abstract constructor.
59+
AbstractTypeStableFunction(f, argtypes::DataType...) = TypeStableFunction(f, argtypes...)
60+
AbstractTypeStableFunction{T}(f) where {T} = TypeStableFunction{T}(f)
61+
62+
# Make instances of TypeStableFunction callable.
63+
@inline (obj::TypeStableFunction{T})(args...; kwds...) where {T} =
64+
as(T, parent(obj)(args...; kwds...))
65+
66+
# Extend base methods.
67+
Base.parent(obj::TypeStableFunction) = getfield(obj, :callable)
68+
69+
Base.return_types(::AbstractTypeStableFunction{T}; kwds...) where {T} = (T,)
70+
Base.return_types(::AbstractTypeStableFunction{T}, ::DataType; kwds...) where {T} = (T,)
71+
72+
Base.promote_op(::AbstractTypeStableFunction{T}, ::DataType...) where {T} = T
73+
74+
for cls in (:AbstractTypeStableFunction, :TypeStableFunction,)
75+
@eval begin
76+
Base.convert(::Type{T}, f::T) where {T<:$(cls)} = f
77+
Base.convert(::Type{$(cls){T}}, f) where {T} = $(cls){T}(f)
78+
end
79+
end

src/macros.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
TypeUtils.@public args...
3+
4+
declares `args...` as being `public` even though they are not exported. For Julia version
5+
< 1.11, this macro does nothing. Using this macro also avoid errors with CI and coverage
6+
tools.
7+
8+
"""
9+
macro public(args::Union{Symbol,Expr}...)
10+
VERSION v"1.11.0-DEV.469" ? esc(Expr(:public, map(
11+
x -> x isa Symbol ? x :
12+
x isa Expr && x.head == :macrocall ? x.args[1] :
13+
error("unexpected argument `$x` to `@public`"), args)...)) : nothing
14+
end
15+
VERSION v"1.11.0-DEV.469" && @public @public
16+
17+
"""
18+
@assert_floating_point A B ...
19+
20+
throws an `ArgumentError` exception if any of the variables `A`, `B`, etc. does not use
21+
floating-point to store its value(s).
22+
23+
See also [`assert_floating_point`](@ref).
24+
25+
"""
26+
macro assert_floating_point(args::Symbol...)
27+
code = [:(assert_floating_point($(QuoteNode(arg)), $(esc(arg)))) for arg in args]
28+
return quote
29+
$(code...)
30+
end
31+
end

0 commit comments

Comments
 (0)