Skip to content

Commit e6d7387

Browse files
authored
Rewrite all @pure functions (#105)
* Now we use `Size` more consistently and rely less on `@pure` implementations of `similar_type`. * `similar` and `similar_type` both use `Size` not integers to discuss size * `similar` and `similar_type` are now streamlined. Users only need to override one definition, just like in Base. * Associated documentation and test changes. * Added some more tests
1 parent 1be47cc commit e6d7387

29 files changed

+429
-526
lines changed

README.md

+38-31
Original file line numberDiff line numberDiff line change
@@ -141,37 +141,6 @@ the methods in Base, we seek to provide a comprehensive support for statically
141141
sized arrays, large or small, that hopefully "just works".
142142

143143
## API Details
144-
### Indexing
145-
146-
Statically sized indexing can be realized by indexing each dimension by a
147-
scalar, an `NTuple{N, Integer}` or `:` (on statically sized arrays only).
148-
Indexing in this way will result a statically sized array (even if the input was
149-
dynamically sized) of the closest type (as defined by `similar_type`).
150-
151-
Conversely, indexing a statically sized array with a dynamically sized index
152-
(such as a `Vector{Integer}` or `UnitRange{Integer}`) will result in a standard
153-
(dynamically sized) `Array`.
154-
155-
### `similar_type()`
156-
157-
Since immutable arrays need to be constructed "all-at-once", we need a way of
158-
obtaining an appropriate constructor if the element type or dimensions of the
159-
output array differs from the input. To this end, `similar_type` is introduced,
160-
behaving just like `similar`, except that it returns a type. Relevant methods
161-
are:
162-
163-
```julia
164-
similar_type{A <: StaticArray}(::Type{A}) # defaults to A
165-
similar_type{A <: StaticArray, ElType}(::Type{A}, ::Type{ElType}) # Change element type
166-
similar_type{A <: StaticArray}(::Type{A}, size::Tuple{Int...}) # Change size
167-
similar_type{A <: StaticArray, ElType}(::Type{A}, ::Type{ElType}, size::Tuple{Int...}) # Change both
168-
```
169-
170-
These setting will affect everything, from indexing, to matrix multiplication
171-
and `broadcast`.
172-
173-
Use of `similar` will fall back to a mutable container, such as a `MVector`
174-
(see below).
175144

176145
### The `Size` trait
177146

@@ -203,6 +172,44 @@ reshape(svector, Size(2,2)) # Convert SVector{4} to SMatrix{2,2}
203172
Size(3,3)(rand(3,3)) # Construct a random 3×3 SizedArray (see below)
204173
```
205174

175+
Users that introduce a new subtype of `StaticArray` should define a (`@pure`)
176+
method for `Size(::Type{NewArrayType})`.
177+
178+
### Indexing
179+
180+
Statically sized indexing can be realized by indexing each dimension by a
181+
scalar, a `StaticVector` or `:`. Indexing in this way will result a statically
182+
sized array (even if the input was dynamically sized, in the case of
183+
`StaticVector` indices) of the closest type (as defined by `similar_type`).
184+
185+
Conversely, indexing a statically sized array with a dynamically sized index
186+
(such as a `Vector{Integer}` or `UnitRange{Integer}`) will result in a standard
187+
(dynamically sized) `Array`.
188+
189+
### `similar_type()`
190+
191+
Since immutable arrays need to be constructed "all-at-once", we need a way of
192+
obtaining an appropriate constructor if the element type or dimensions of the
193+
output array differs from the input. To this end, `similar_type` is introduced,
194+
behaving just like `similar`, except that it returns a type. Relevant methods
195+
are:
196+
197+
```julia
198+
similar_type{A <: StaticArray}(::Type{A}) # defaults to A
199+
similar_type{A <: StaticArray, ElType}(::Type{A}, ::Type{ElType}) # Change element type
200+
similar_type{A <: AbstractArray}(::Type{A}, size::Size) # Change size
201+
similar_type{A <: AbstractArray, ElType}(::Type{A}, ::Type{ElType}, size::Size) # Change both
202+
```
203+
204+
These setting will affect everything, from indexing, to matrix multiplication
205+
and `broadcast`. Users wanting introduce a new array type should *only* overload
206+
the last method in the above.
207+
208+
Use of `similar` will fall back to a mutable container, such as a `MVector`
209+
(see below), and it requires use of the `Size` trait if you wish to set a new
210+
static size (or else a dynamically sized `Array` will be generated when
211+
specifying the size as plain integers).
212+
206213
### `SVector`
207214

208215
The simplest static array is the `SVector`, defined as

src/FieldVector.jl

+6-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""
22
abstract FieldVector{T} <: StaticVector{T}
33
4-
Inheriting from this type will make it easy to create your own vector types.
5-
A `FieldVector` will automatically determine its size from the number of fields
6-
(or it can be overriden by `size()`), and define `getindex` and `setindex!`
7-
appropriately. An immutable `FieldVector` will be as performant as an `SVector`
8-
of similar length and element type, while a mutable `FieldVector` will behave
9-
similarly to an `MVector`.
4+
Inheriting from this type will make it easy to create your own vector types. A `FieldVector`
5+
will automatically determine its size from the number of fields, and define `getindex` and
6+
`setindex!` appropriately. An immutable `FieldVector` will be as performant as an `SVector`
7+
of similar length and element type, while a mutable `FieldVector` will behave similarly to
8+
an `MVector`.
109
1110
For example:
1211
@@ -21,10 +20,7 @@ abstract FieldVector{T} <: StaticVector{T}
2120
# Is this a good idea?? Should people just define constructors that accept tuples?
2221
@inline (::Type{FV}){FV<:FieldVector}(x::Tuple) = FV(x...)
2322

24-
@pure length{FV<:FieldVector}(::FV) = nfields(FV)
25-
@pure length{FV<:FieldVector}(::Type{FV}) = nfields(FV)
26-
@pure size{FV<:FieldVector}(::FV) = (length(FV),)
27-
@pure size{FV<:FieldVector}(::Type{FV}) = (length(FV),)
23+
@pure Size{FV<:FieldVector}(::Type{FV}) = Size(nfields(FV))
2824

2925
@inline getindex(v::FieldVector, i::Integer) = getfield(v, i)
3026
@inline setindex!(v::FieldVector, x, i::Integer) = setfield!(v, i, x)

src/FixedSizeArrays.jl

+11-17
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ macro fixed_vector(name, parent)
132132
end
133133
end
134134

135-
Base.@pure Base.size{S}(::Union{$(name){S}, Type{$(name){S}}}) = (S, )
136-
Base.@pure Base.size{S,T}(::Type{$(name){S, T}}) = (S,)
135+
Base.@pure StaticArrays.Size{S}(::Type{$(name){S}}) = Size(S)
136+
Base.@pure StaticArrays.Size{S,T}(::Type{$(name){S, T}}) = Size(S)
137137

138138
Base.@propagate_inbounds function Base.getindex(v::$(name), i::Integer)
139139
v.data[i]
@@ -144,22 +144,16 @@ macro fixed_vector(name, parent)
144144
@inline function Base.convert{S, T}(::Type{$(name){S, T}}, x::Tuple)
145145
$(name){S, T}(convert(NTuple{S, T}, x))
146146
end
147-
# StaticArrays.similar_type{SV <: $(name)}(::Union{SV, Type{SV}}) = $(name)
148-
# function StaticArrays.similar_type{SV <: $(name), T}(::Union{SV, Type{SV}}, ::Type{T})
149-
# $(name){length(SV), T}
150-
# end
151-
# function StaticArrays.similar_type{SV <: $(name)}(::Union{SV, Type{SV}}, s::Tuple{Int})
152-
# $(name){s[1], eltype(SV)}
153-
# end
154-
function StaticArrays.similar_type{SV <: $(name), T}(::Union{SV, Type{SV}}, ::Type{T}, s::Tuple{Int})
155-
$(name){s[1], T}
156-
end
157-
function StaticArrays.similar_type{SV <: $(name)}(::Union{SV, Type{SV}}, s::Tuple{Int})
158-
$(name){s[1], eltype(SV)}
159-
end
160-
function StaticArrays.similar_type{SV <: $(name), T}(::Union{SV, Type{SV}}, ::Type{T})
161-
$(name){length(SV), T}
147+
148+
@generated function StaticArrays.similar_type{SV <: $(name), T,S}(::Type{SV}, ::Type{T}, s::Size{S})
149+
if length(S) === 1
150+
$(name){S[1], T}
151+
else
152+
StaticArrays.default_similar_type(T,s(),Val{length(S)})
153+
end
162154
end
155+
156+
163157
eltype_or(::Type{$(name)}, or) = or
164158
eltype_or{T}(::Type{$(name){TypeVar(:S), T}}, or) = T
165159
eltype_or{S}(::Type{$(name){S, TypeVar(:T)}}, or) = or

src/MArray.jl

+4-6
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,10 @@ end
9494
## MArray methods ##
9595
####################
9696

97-
similar_type{M,T,N,L,S}(::Type{MArray{M,T,N,L}}, ::Type{S}) = MArray{M,S,N,L}
98-
99-
@pure size{Size}(::Type{MArray{Size}}) = Size
100-
@pure size{Size,T}(::Type{MArray{Size,T}}) = Size
101-
@pure size{Size,T,N}(::Type{MArray{Size,T,N}}) = Size
102-
@pure size{Size,T,N,L}(::Type{MArray{Size,T,N,L}}) = Size
97+
@pure Size{S}(::Type{MArray{S}}) = Size(S)
98+
@pure Size{S,T}(::Type{MArray{S,T}}) = Size(S)
99+
@pure Size{S,T,N}(::Type{MArray{S,T,N}}) = Size(S)
100+
@pure Size{S,T,N,L}(::Type{MArray{S,T,N,L}}) = Size(S)
103101

104102
function getindex(v::MArray, i::Integer)
105103
Base.@_inline_meta

src/MMatrix.jl

+3-6
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,9 @@ end
102102
## MMatrix methods ##
103103
#####################
104104

105-
similar_type{T,N,M,L,S}(::Type{MMatrix{N,M,T,L}}, ::Type{S}) = MMatrix{M,N,S,L}
106-
similar_type{T,N,M,L,S}(::Type{MMatrix{N,M,T,L}}, ::Type{S}, Size::Tuple{Int}) = MVector{Size[1],S}
107-
108-
@pure size{S1,S2}(::Type{MMatrix{S1,S2}}) = (S1, S2)
109-
@pure size{S1,S2,T}(::Type{MMatrix{S1,S2,T}}) = (S1, S2)
110-
@pure size{S1,S2,T,L}(::Type{MMatrix{S1,S2,T,L}}) = (S1, S2)
105+
@pure Size{S1,S2}(::Type{MMatrix{S1,S2}}) = Size(S1, S2)
106+
@pure Size{S1,S2,T}(::Type{MMatrix{S1,S2,T}}) = Size(S1, S2)
107+
@pure Size{S1,S2,T,L}(::Type{MMatrix{S1,S2,T,L}}) = Size(S1, S2)
111108

112109
@propagate_inbounds function getindex{S1,S2,T}(m::MMatrix{S1,S2,T}, i::Integer)
113110
#@boundscheck if i < 1 || i > length(m)

src/MVector.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@ end
4646
## MVector methods ##
4747
#####################
4848

49-
similar_type{T,N,S}(::Type{MVector{N,T}}, ::Type{S}) = MVector{N,S}
50-
51-
@pure size{S}(::Type{MVector{S}}) = (S, )
52-
@pure size{S,T}(::Type{MVector{S,T}}) = (S,)
49+
@pure Size{S}(::Type{MVector{S}}) = Size(S)
50+
@pure Size{S,T}(::Type{MVector{S,T}}) = Size(S)
5351

5452
@propagate_inbounds function getindex(v::MVector, i::Integer)
5553
v.data[i]

src/SArray.jl

+4-6
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,10 @@ end
7474
## SArray methods ##
7575
####################
7676

77-
similar_type{M,T,N,L,S}(::Type{SArray{M,T,N,L}}, ::Type{S}) = SArray{M,S,N,L}
78-
79-
@pure size{Size}(::Type{SArray{Size}}) = Size
80-
@pure size{Size,T}(::Type{SArray{Size,T}}) = Size
81-
@pure size{Size,T,N}(::Type{SArray{Size,T,N}}) = Size
82-
@pure size{Size,T,N,L}(::Type{SArray{Size,T,N,L}}) = Size
77+
@pure Size{S}(::Type{SArray{S}}) = Size(S)
78+
@pure Size{S,T}(::Type{SArray{S,T}}) = Size(S)
79+
@pure Size{S,T,N}(::Type{SArray{S,T,N}}) = Size(S)
80+
@pure Size{S,T,N,L}(::Type{SArray{S,T,N,L}}) = Size(S)
8381

8482
function getindex(v::SArray, i::Integer)
8583
Base.@_inline_meta

src/SMatrix.jl

+3-6
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,9 @@ end
116116
## SMatrix methods ##
117117
#####################
118118

119-
similar_type{T,N,M,L,S}(::Type{SMatrix{N,M,T,L}}, ::Type{S}) = SMatrix{N, M, S, L}
120-
similar_type{T,N,M,L,S}(::Type{SMatrix{N,M,T,L}}, ::Type{S}, Size::Tuple{Int}) = SVector{Size[1],S}
121-
122-
@pure size{S1,S2}(::Type{SMatrix{S1,S2}}) = (S1, S2)
123-
@pure size{S1,S2,T}(::Type{SMatrix{S1,S2,T}}) = (S1, S2)
124-
@pure size{S1,S2,T,L}(::Type{SMatrix{S1,S2,T,L}}) = (S1, S2)
119+
@pure Size{S1,S2}(::Type{SMatrix{S1,S2}}) = Size(S1, S2)
120+
@pure Size{S1,S2,T}(::Type{SMatrix{S1,S2,T}}) = Size(S1, S2)
121+
@pure Size{S1,S2,T,L}(::Type{SMatrix{S1,S2,T,L}}) = Size(S1, S2)
125122

126123
function getindex(v::SMatrix, i::Integer)
127124
Base.@_inline_meta

src/SVector.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@ end
4040
## SVector methods ##
4141
#####################
4242

43-
similar_type{T,N,S}(::Type{SVector{N,T}}, ::Type{S}) = SVector{N,S}
44-
45-
@pure size{S}(::Type{SVector{S}}) = (S, )
46-
@pure size{S,T}(::Type{SVector{S,T}}) = (S,)
43+
@pure Size{S}(::Type{SVector{S}}) = Size(S)
44+
@pure Size{S,T}(::Type{SVector{S,T}}) = Size(S)
4745

4846
@propagate_inbounds function getindex(v::SVector, i::Integer)
4947
v.data[i]

src/Scalar.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ end
1010

1111
@inline (::Type{Scalar{T}}){T}(x::Tuple{T}) = Scalar{T}(x[1])
1212

13-
similar_type{T,S}(::Type{Scalar{T}}, ::Type{S}) = Scalar{S}
14-
15-
@pure size(::Type{Scalar}) = ()
16-
@pure size{T}(::Type{Scalar{T}}) = ()
13+
@pure Size(::Type{Scalar}) = Size()
14+
@pure Size{T}(::Type{Scalar{T}}) = Size()
1715

1816
getindex(v::Scalar) = v.data
1917
@inline function getindex(v::Scalar, i::Int)

src/SizedArray.jl

+6-8
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ end
4848
@inline (::Type{SizedArray{S,T}}){S,T}(x::Tuple) = SizedArray{S,T,_dims(S),_dims(S)}(x)
4949
@inline (::Type{SizedArray{S}}){S,T,L}(x::NTuple{L,T}) = SizedArray{S,T,_dims(S),_dims(S)}(x)
5050

51-
similar_type{S,T,N,M,R}(::Type{SizedArray{S,T,N,M}}, ::Type{R}) = SizedArray{S,R,N,M}
52-
5351
# Overide some problematic default behaviour
5452
@inline convert{SA<:SizedArray}(::Type{SA}, sa::SizedArray) = SA(sa.data)
5553
@inline convert{SA<:SizedArray}(::Type{SA}, sa::SA) = sa
@@ -65,23 +63,23 @@ similar_type{S,T,N,M,R}(::Type{SizedArray{S,T,N,M}}, ::Type{R}) = SizedArray{S,R
6563

6664
@pure _ndims{N}(::NTuple{N,Int}) = N
6765

68-
@pure size{S}(::Type{SizedArray{S}}) = S
69-
@pure size{S,T}(::Type{SizedArray{S,T}}) = S
70-
@pure size{S,T,N}(::Type{SizedArray{S,T,N}}) = S
71-
@pure size{S,T,N,M}(::Type{SizedArray{S,T,N,M}}) = S
66+
@pure Size{S}(::Type{SizedArray{S}}) = Size(S)
67+
@pure Size{S,T}(::Type{SizedArray{S,T}}) = Size(S)
68+
@pure Size{S,T,N}(::Type{SizedArray{S,T,N}}) = Size(S)
69+
@pure Size{S,T,N,M}(::Type{SizedArray{S,T,N,M}}) = Size(S)
7270

7371
@propagate_inbounds getindex(a::SizedArray, i::Int) = getindex(a.data, i)
7472
@propagate_inbounds setindex!(a::SizedArray, v, i::Int) = setindex!(a.data, v, i)
7573

7674
typealias SizedVector{S,T,M} SizedArray{S,T,1,M}
77-
@pure size{S}(::Type{SizedVector{S}}) = S
75+
@pure Size{S}(::Type{SizedVector{S}}) = Size(S)
7876
@inline (::Type{SizedVector{S}}){S,T,M}(a::Array{T,M}) = SizedArray{S,T,1,M}(a)
7977
@inline (::Type{SizedVector{S}}){S,T,L}(x::NTuple{L,T}) = SizedArray{S,T,1,1}(x)
8078
@inline (::Type{Vector})(sa::SizedVector) = sa.data
8179
@inline convert(::Type{Vector}, sa::SizedVector) = sa.data
8280

8381
typealias SizedMatrix{S,T,M} SizedArray{S,T,2,M}
84-
@pure size{S}(::Type{SizedMatrix{S}}) = S
82+
@pure Size{S}(::Type{SizedMatrix{S}}) = Size(S)
8583
@inline (::Type{SizedMatrix{S}}){S,T,M}(a::Array{T,M}) = SizedArray{S,T,2,M}(a)
8684
@inline (::Type{SizedMatrix{S}}){S,T,L}(x::NTuple{L,T}) = SizedArray{S,T,2,2}(x)
8785
@inline (::Type{Matrix})(sa::SizedMatrix) = sa.data

src/StaticArrays.jl

+2-13
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export Size
2121
export @SVector, @SMatrix, @SArray
2222
export @MVector, @MMatrix, @MArray
2323

24-
export similar_type, setindex
24+
export similar_type
25+
export push, pop, shift, unshift, insert, deleteat, setindex
2526

2627
include("util.jl")
2728

@@ -55,17 +56,5 @@ if VERSION < v"0.6.0-dev.1671"
5556
end
5657
include("ImmutableArrays.jl")
5758

58-
# TODO list
59-
# ---------
60-
#
61-
# * more tests
62-
#
63-
# * reshape() - accept Val? Currently uses `ReshapedArray`. Cool :)
64-
#
65-
# * permutedims() - accept Val? Or wait for `PermutedDimsArray` ?
66-
#
67-
# * Linear algebra - matrix functions (det, inv, eig, svd, qr, etc...)
68-
# (currently, we use pointers to interact with LAPACK, etc)
69-
7059

7160
end # module

0 commit comments

Comments
 (0)