Using a Custom Length Vector type to define function Parameters

I want to dispatch 2 different functions for
f(vect::VectorLength2)
and
f(vect::VectorLength3)

Basically, is there any way to have a type that has this behaviour:

typeof([1,2]) <: VectorLength2
> true
typeof([1,2,3]) <: VectorLength2
> false

I don’t want to wrap [1,2,3] vector into some custom function that would have a custom type.

Is there a way perhaps to have something like Tuple{Real, Real} that would define Vector Length?

This might be dumb but I tried

typeof([1,2,3]) <: SizedVector{3}
> false

Any way to do this?

The type SVector from StaticArrays.jl has this vector-like property (it essentially wraps an NTuple).

I’m assuming you mean to use SVector instead of SizedVector in my code.
Unfortunately doesn’t work.

As far as wrapping my vector into SVector to have a well-defined array length works… I’m creating something like… say My own package.
The user would have to then import StaticArrays.jl in order to use my functions, and that isn’t something I want.

Any way to do it without wrapping?
Any property of the Vector that we can take advantage of to identify the type?

1 thing we can do is say all right…

If length(Vector) == 2
    do this
else
    do that
end

or something along those lines. But just checking the web if there is some standard way to do this.

To give you an idea of what I’m up to…
If the user inputs a vector of len 2, I’ll set other attributes to those of a 2-dimensional object, and likewise for len = 3.

I don’t want the input to have to deal with a third package. Just simply input a Vector.

If you create a package and declare StaticArrays.jl as a dependency, this is transparent to the user. They don’t need to load that package themselves explicitly. But you’re right that it will be loaded implicitly. Are you worried about the weight of the dependency?

StaticArrays.jl is a really standard and optimized way of dealing with small vectors, typically 2d and 3d. If you’re worried about performance, I would suggest trying it out instead of rolling out your own solution.

I guess it’ll be well-optimised to not have too much of an impact on performance.

I couldn’t really figure out how to make that distinction though.

typeof([1,2,3]) <: SVector{3}
> false

This doesn’t work either.

This does not work, because [1,2,3] is a vector not a static vector. But StaticVector.jl has a nice short form of construction so

> typeof(SA[1,2,3]) <: SVector{3}
true

The simple reason is, that StaticArrays.jl does not overload the default vector constructor, since that would be a (very very heavy) type piracy). But the SA prefix makes code nearly as neat.

does work. To illustrate the dispatch I understood you want, take a look at

using StaticArrays
f(sa::SVector{2}) = "A"
f(sa::SVector{3}) = "B"

yields

julia> [f(SA[1,2]), f(SA[1,2,3])]
2-element Vector{String}:
 "A"
 "B"

so a nice and easy dispatch on length.

1 Like

Ahhhh so we pretty much have to use a third-party definition itself.
That was honestly something I didn’t want to do.

I like the way Makie lets you input anything to plot… Points, Tuples, Vectors, you name it!
Was thinking more like… without the user needing to think much, whatever type he/she feels comfortable working with in his/her particular use case, they can use that.

I guess this is it tho… SA functioning is all there is to use to have the functionality I want.

Or just curious… can we have something like…

F(x::Vect{Real}) where length(x)==3

If you don’t want to require the user to explicitly import StaticArrays.jl just to create the arrays to put into your functions you can always eat the type-instability in the “interface layer” of your package. I mean something like:

# If the user provided a static array
f(sa::SVector{2}) = "A"
f(sa::SVector{3}) = "B"

# if the user provided a normal vector
f(v::Vector) = f(SVector{length(v)}(v)) # causes 1 dynamic dispatch

This is a very common design pattern in Julia. Most “setup” function in e.g. DifferentialEquations.jl contain type instabilities, e.g. when you ask DE to pick a reasonable default solver.
Edit: I am somewhat assuming, that you would use these vectors for some longer computation. If not, then you should just use if with branches I suppose.

2 Likes

Hey! Thank works exactly the way I want stuff to.

Any caveats? Would it cause me some issues?

The where you propose is not possible.
The main problem is that vectors can grow arbitrarily, so when they should keep track of their length to dispatch on (that is not calling length but more SA like) you have no other chance than wrapping that.

Makie internally definetly checks the length a few times, and I do agree, for user facing functions such flexibility is nice; however, internally, both for readability and code maintenance, the StaticArray approach is probably preferable (in my opinion it definelty is) – thesis basically what @abreamer proposes as well – for the user it is nice, they can still pass normal vectors.

Sure you can avoid third party code – by implementing such types yourself. In my experience, Julia is modular, which has the large advantage, that usually people more clever than me come up with solutions that I can nicely use (and avoid reinventing the wheel).

Well calling this function with a normal Array causes a dynamic dispatch, which comes with some performance penalty and causes a few memory allocations. As long as the user does not call this function in a tight inner loop that must be 100% efficient, it is fine. However, if performance is critical then the user can just switch to StaticArrays and everything is fine.

All right, I understand that, sir. Thank you!

Hmmm…
I don’t think I’ll need to use it that often.
Honestly, I need it for like a fraction of a second lol.
I would take whatever input the user has and convert it to an Observable Point type for internal work.

So I just need this kind of mechanism to distinguish between 2-element and 3-element vectors during dispatch.

And there are only these 2 cases? Honestly, I’d just use a plain, boring if length(v) == 2. Everything else seems kinda over-engineered at this point. This also has the advantage that you don’t need to worry about anything with performance and it is as easy as it gets for the compiler.

using StaticArrays
# f(v::Vector) = f(SVector{length(v)}(v))
mutable struct P
    x
    # If the user provided a static array
    f(v::Vector) = f(SVector{length(v)}(v))

    function P(x::SVector{2})
        new("A")
    end

    function P(x::SVector{3})
        new("B")
    end
end

this doesn’t work :sob:

I mean yeah, I guess this wont.

I do have some more questions tbh…

mutable struct P
    x

    function P(x::Vector)
        if length(x) == 2
            new("A")
        elseif length(x) == 3
            new("B")
        else 
            throw(TypeError)
        end
    end
    function P(x::SVector{2})
        new("A")
    end

    function P(x::SVector{3})
        new("B")
    end
end

Is this efficient? Or is there any benefit for Multiple Dispatch for this use case?

This is literally all I would need (To be honest, with that little if statement, wouldn’t need to deal with static arrays in the first place)

I’ll just have an Additional set of definitions for Vectors. What say?
(sorry for pinning your but… @abraemer @kellertuer)

Well you have a f in there. That probably should have been P. Try:

mutable struct P
    x
    P(v::Vector) = P(SVector{length(v)}(v))
    P(x::SVector{2}) = new("A")
    P(x::SVector{3}) = new("B")
end

If you mean the construction, then I’d say yes.
Here the issue is more that the field is untyped which is generally bad for performance. However, this might not matter for your case and be a perfectly reasonable design choice. Runtime performance is not the only design goal after all :slight_smile:

I agree: I don’t think it is reasonable to use StaticArrays.jl just for a singular dispatch with two options. So just ditch SA and keep the if.

1 Like

Cool!

All right I might sound crazy but I ran my personal benchmarks (using BenchmarkTools) multiple times and found that defining the types for structs in the Function is more efficient than defining them in the definition (if you get what I’m trying to convey).

Lol. Idk how true that is in general. But at least that’s what I found to be true for my Struct definitions.

Worked! Thank you!

Although, as you said, I feel it’s not worth using Static Arrays. Might be better to use an If statement.

However, would still put your code as the answer to the Question I had! Thank you!!
Thank you @abraemer and @gdalle for your help too!

No I don’t really get what you mean, but feel free to open a new topic and post the benchmark code. Putting types in the place where you use some untyped field of a struct might recover some performance, but generally typed fields should be as fast as it gets. So you likely had a small mistake in your benchmark.

But as I said: If performance is not critical or less important than simplicity of the implementation, then you don’t need to put types on fields in your struct.

All right just decided, that using StaticArrays.jl is simpler and convenient instead of literally reiterating everything.

I would have had to do something like

If something isa Vector
do something

for everything.
I had 2 dispatches, 1 more 2D, 1 for 3D.

I just added the f(v::Vector) = f(StaticArray) thingy for my Struct, Added StaticArray{} in the Union for my defining function arguments and now everything works incredibly well.

hmmm… does lose a bit on the speed but that isn’t that bad I’d say… (4x the time taken)

I mean for the user, who just has to define things once and for all and then just use Observables to control things thereafter, that doesn’t matter if?

As for internal definitions, I’ll be sure to Use the Quickest method.

Thank you anyways!