 # Confused with type definition

Hello there, say I want to create a new type which is basically the same of `Array{Float64, 1}` except for the behaviour for one operator (for example `+`).

What I’m doing now is the following. First create a new mutable type `MyVec`

``````struct MyVec
v::Array{Float64, 1}
end
``````

``````Base.:+(v::MyVec, u::MyVec) = MyVec(v.v .+ u.v[end:-1:1])
``````

Now the problem I’m facing is that the rest of the operators shouldn’t change their behaviour, but I’m forced to re-define all of them.

Is it possible to tell Julia that `MyVec` is basically an `Array{Float64, 1}` so that I can “inherit” all the `Array` behaviour and simply add the new one?

That feature does not exist in Julia itself. There are approaches that can ease the work.

``````import Base: length
import LinearAlgebra: dot

struct MyVec{T} <: AbstractVector{T}
vec::Vector{T}
end

# required by the AbstractArray interface
Base.size(x::MyVec) = size(x.vec)
Base.getindex(x::MyVec, i::Int) = getindex(x.vec, i)
Base.setindex!(x::MyVec, v, i::Int) = setindex!(x.vec, v, i)

Base.:(+)(v::MyVec, u::MyVec) = ...
Base.:(+)(v::MyVec, u::Vector{Float64}) = v + MyVec(u)
Base.:(+)(v::Vector{Float64}, u::MyVec) = MyVec(v) + u

for F in (:length,) # add other appropriate unary functions
@eval begin
\$F(v::MyVec,) = length(v.v)
end
end

for F in (:dot,) # add other appropriate binary functions
@eval begin
\$F(v::MyVec, u::MyVec) = \$F(v.v, u.v)
\$F(v::MyVec, u::Vector{Float64}) = \$F(v.v, u)
\$F(v::Vector{Float64}, u::MyVec) = \$F(v, u.v)
end
end
````````

Why not just define a function

``````add(x::Vector{T}, y::Vector{T}) where {T} =
dot(x,y) + dot(y, reverse(x))
``````

or whatever you want it to do –
rather than needing all the special type support

1 Like

I see, thanks @JeffreySarnoff for the `@eval` example.

Creating a new type seems a way to keep the code cleaner (I also don’t want to change `show` behaviour for `Array`) but I’ll probably stick to `Array` and create only the new functions

Consider also doing `MyVec <: AbstractVector{Float64}`

1 Like

would that be in the struct definition, like

``````struct MyVec <: AbstractVector{Float64}
v::Vector{Float64}
end
``````

?

Yes, in this way your new datastructure can be used in all methods which accept AbstractVector

edited my example accordingly

not sure why it’s not working in the REPL tho

``````julia> struct MyVec <: AbstractVector{Float64}
v::Vector{Float64}
end

julia> MyVec([0.0, 0.0, 0.0])
Error showing value of type MyVec:
ERROR: MethodError: no method matching size(::MyVec)
Closest candidates are:
size(::AbstractArray{T,N}, ::Any) where {T, N} at abstractarray.jl:38
size(::Base.AsyncGenerator) at asyncmap.jl:409
size(::Core.Compiler.StmtRange) at show.jl:1874
...
``````

It does not work (or rather, cannot be printed) because a subtype of `AbstractVector` must implement some methods, see here.

That happens on showing the structure, add `;` at the end of the line to not show it. To fully implement the iteration interface see Interfaces · The Julia Language

I see!

thanks for pointing out the interfaces docs page

I am kinda surprised nobody mentioned Lazy.jl and its macro `@forward`.

Basically you can do:

``````import Base: length, getindex, setindex
using Lazy: @forward

struct MyVec <: AbstractVector{Float64}
v::Vector{Float64} # same as Array{Float64, 1}
end

@forward MyVec.v Base.size, Base.getindex, Base.setindex!
``````

For all function you just delegate to the field.

12 Likes

thanks @Henrique_Becker !

`@forward` provides the cherry on top of the previous answers 1 Like