Skip to content

Commit 98d35ff

Browse files
authored
Implement SA[...] syntax for SArray literals (#633)
This finally gives a very attractive syntax for short static array (SVector and SMatrix) literals. The main downside is that this is a syntax pun on indexing and typed array construction: the result of the `SA[...]` depends on whether the name `SA` is bound to `StaticArrays.SA` or is local variable of some other type. However, this is no worse than Int[1,2,3], which is also a pun on the array indexing syntax. Cases where this syntax could refer to something other than StaticArrays.SA are detectable by the compiler and in principle also accessible to a linter.
1 parent f669465 commit 98d35ff

File tree

4 files changed

+82
-0
lines changed

4 files changed

+82
-0
lines changed

src/StaticArrays.jl

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export SHermitianCompact
3636

3737
export Size, Length
3838

39+
export SA
3940
export @SVector, @SMatrix, @SArray
4041
export @MVector, @MMatrix, @MArray
4142

@@ -113,6 +114,7 @@ include("SizedArray.jl")
113114
include("SDiagonal.jl")
114115
include("SHermitianCompact.jl")
115116

117+
include("initializers.jl")
116118
include("convert.jl")
117119

118120
include("abstractarray.jl")

src/initializers.jl

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
SA[ elements ]
3+
SA{T}[ elements ]
4+
5+
Create `SArray` literals using array construction syntax. The element type is
6+
inferred by promoting `elements` to a common type or set to `T` when `T` is
7+
provided explicitly.
8+
9+
# Examples:
10+
11+
* `SA[1.0, 2.0]` creates a length-2 `SVector` of `Float64` elements.
12+
* `SA[1 2; 3 4]` creates a 2×2 SMatrix of `Int`s.
13+
* `SA[1 2]` creates a 1×2 SMatrix of `Int`s.
14+
* `SA{Float32}[1, 2]` creates a length-2 `SVector` of `Float32` elements.
15+
"""
16+
struct SA{T} ; end
17+
18+
@inline similar_type(::Type{SA}, ::Size{S}) where {S} = SArray{Tuple{S...}}
19+
@inline similar_type(::Type{SA{T}}, ::Size{S}) where {T,S} = SArray{Tuple{S...}, T}
20+
21+
Base.@pure _SA_type(sa::Type{SA}, len::Int) = SVector{len}
22+
Base.@pure _SA_type(sa::Type{SA{T}}, len::Int) where {T} = SVector{len,T}
23+
24+
@inline Base.getindex(sa::Type{<:SA}, xs...) where T = similar_type(sa, Size(length(xs)))(xs)
25+
@inline Base.typed_vcat(sa::Type{<:SA}, xs::Number...) where T = similar_type(sa, Size(length(xs)))(xs)
26+
@inline Base.typed_hcat(sa::Type{<:SA}, xs::Number...) where T = similar_type(sa, Size(1,length(xs)))(xs)
27+
28+
Base.@pure function _SA_hvcat_transposed_size(rows)
29+
M = rows[1]
30+
if any(r->r != M, rows)
31+
# @pure may not throw... probably. See
32+
# https://discourse.julialang.org/t/can-pure-functions-throw-an-error/18459
33+
return nothing
34+
end
35+
Size(M, length(rows))
36+
end
37+
38+
@inline function Base.typed_hvcat(sa::Type{<:SA}, rows::Dims, xs::Number...) where T
39+
msize = _SA_hvcat_transposed_size(rows)
40+
if msize === nothing
41+
throw(ArgumentError("SA[...] matrix rows of length $rows are inconsistent"))
42+
end
43+
# hvcat lowering is row major ordering, so we must transpose
44+
transpose(similar_type(sa, msize)(xs))
45+
end
46+

test/initializers.jl

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
SA_test_ref(x) = SA[1,x,x]
2+
SA_test_ref(x,T) = SA{T}[1,x,x]
3+
@test @inferred(SA_test_ref(2)) === SVector{3,Int}((1,2,2))
4+
@test @inferred(SA_test_ref(2.0)) === SVector{3,Float64}((1,2,2))
5+
@test @inferred(SA_test_ref(2,Float32)) === SVector{3,Float32}((1,2,2))
6+
7+
SA_test_vcat(x) = SA[1;x;x]
8+
SA_test_vcat(x,T) = SA{T}[1;x;x]
9+
@test @inferred(SA_test_vcat(2)) === SVector{3,Int}((1,2,2))
10+
@test @inferred(SA_test_vcat(2.0)) === SVector{3,Float64}((1,2,2))
11+
@test @inferred(SA_test_vcat(2,Float32)) === SVector{3,Float32}((1,2,2))
12+
13+
SA_test_hcat(x) = SA[1 x x]
14+
SA_test_hcat(x,T) = SA{T}[1 x x]
15+
@test @inferred(SA_test_hcat(2)) === SMatrix{1,3,Int}((1,2,2))
16+
@test @inferred(SA_test_hcat(2.0)) === SMatrix{1,3,Float64}((1,2,2))
17+
@test @inferred(SA_test_hcat(2,Float32)) === SMatrix{1,3,Float32}((1,2,2))
18+
19+
SA_test_hvcat(x) = SA[1 x x;
20+
x 2 x]
21+
SA_test_hvcat(x,T) = SA{T}[1 x x;
22+
x 2 x]
23+
@test @inferred(SA_test_hvcat(3)) === SMatrix{2,3,Int}((1,3,3,2,3,3))
24+
@test @inferred(SA_test_hvcat(3.0)) === SMatrix{2,3,Float64}((1,3,3,2,3,3))
25+
@test @inferred(SA_test_hvcat(1.0im)) === SMatrix{2,3,ComplexF64}((1,1im,1im,2,1im,1im))
26+
@test @inferred(SA_test_hvcat(3,Float32)) === SMatrix{2,3,Float32}((1,3,3,2,3,3))
27+
28+
@test SA[1] === SVector{1,Int}((1))
29+
30+
@test_throws ArgumentError("SA[...] matrix rows of length (3, 2) are inconsistent") SA[1 2 3;
31+
4 5]
32+
@test_throws ArgumentError("SA[...] matrix rows of length (2, 3) are inconsistent") SA[1 2;
33+
3 4 5]

test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ include("convert.jl")
3131
include("core.jl")
3232
include("abstractarray.jl")
3333
include("indexing.jl")
34+
include("initializers.jl")
3435
Random.seed!(42); include("mapreduce.jl")
3536
Random.seed!(42); include("arraymath.jl")
3637
include("broadcast.jl")

0 commit comments

Comments
 (0)