Nero gris
Nero gris

Reputation: 582

Julia: Making a type for a specific size of vector

I am trying to define a type (structure?) in Julia for a vector with three elements. I think the closest thing I found Optimally passing dimensions of fixed size array in julia and Declare the size of an array attribute in a type definition, however these are pre-0.6 since immutables are no longer a thing. Also, it just seems wrong.

The use case is that I know the size of the vectors my function will be taking and want to have

function myFunc(v::threeVec,u::threeVec)
    Do stuff to u and v
end

Further searching led me constructors. https://docs.julialang.org/en/stable/manual/constructors/ In particular I saw the example

struct OrderedPair
           x::Real
           y::Real
           OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
       end

However this is a separate object, and even then, I'm not sure how I'd pass something like that into a function. I considered instead using triples since those have type Tuple(Int,Int,Int) however I'm going to be doing vector/matrix arithmetic on u and v so I'm rather not have to convert them.

I could have the vector length checked inside the function, but I read in the tips that its preferred that you use types for this due to the dispatcher. For this particular function, that is a reasonable way of doing it in this case, but in other use cases this might not be such a great idea so I'd like to do it the "right way" now.

Upvotes: 9

Views: 6478

Answers (1)

Harrison Grodin
Harrison Grodin

Reputation: 2313

There are many ways to deal with this type of scenario; I'll outline a few of them below based on the ideas you provided.

Option 1: StaticArrays

The StaticArrays.jl package provides support for fixed-length arrays.

using StaticArrays
const ThreeVec{T} = SVector{3,T}

function myFunc(u::ThreeVec, v::ThreeVec)
    u .+ v  # example functionality
end

This implementation allows myFunc to be called only when both arguments are SVectors of length 3.

julia> myFunc(SVector(1, 2, 3), SVector(4, 5, 6))
3-element SArray{Tuple{3},Int64,1,3}:
 5
 7
 9

julia> myFunc(SVector(1, 2, 3), SVector(4, 5))
ERROR: MethodError: no method matching myFunc(::SArray{Tuple{3},Int64,1,3}, ::SArray{Tuple{2},Int64,1,2})
Closest candidates are:
  myFunc(::SArray{Tuple{3},T,1,3} where T, ::SArray{Tuple{3},T,1,3} where T) at REPL[13]:2

It's also possible to define a custom type with an inner constructor to assert that the length is correct. However, in addition to potential inefficiencies, this will require that you overload various methods to support your custom type, which StaticArrays already handles.

Option 2: Tuples

Depending on what vector/matrix arithmetic you plan on performing, it's possible that tuples will already support the functionality natively through broadcasting. For example, although tuples can't be added, addition can be broadcasted over their elements.

julia> u = (1, 2, 3);
       v = (4, 5, 6);

julia> u + v  # not allowed
ERROR: MethodError: no method matching +(::Tuple{Int64,Int64,Int64}, ::Tuple{Int64,Int64,Int64})
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:502
Stacktrace:
 [1] top-level scope at none:0

julia> u .+ v  # element-wise broadcasting
(5, 7, 9)

Option 3: Runtime Error

If you would like to operate on the built-in Vector type, you can simply throw an error whenever the input is invalid, moving your error handling from compilation to runtime.

function myFunc(u::Vector, v::Vector)
    length(u) == 3 || throw(ArgumentError("Invalid length of (u = $u), should be 3"))
    length(v) == 3 || throw(ArgumentError("Invalid length of (v = $v), should be 3"))
    u .+ v  # example functionality
end

Upvotes: 15

Related Questions