Help to create a macro / overload to mimic setindex! behavior in SArrays

Hi guys!

I am trying to use SArrays in my project. I have tons of vectors and matrices that, in some cases, I need to modify only one index. What I am doing is to replace:

Trw[i] = T(0)

to

Trw = setindex(Trw, 0, i)

But it is somewhat strange. I am wondering if it is possible to overload setindex! or create a macro to make things easier.

I tried somethings without much success. Can anyone help me?

I wouldn’t recommend overloading setindex!, as it won’t have the expected behavior of mutating the object, but will instead just be replacing it. Also, overloading a function you don’t own (Base.setindex!) on a type you don’t own (SArray) is type-piracy and is not recommended because it can silently change the behavior other people’s code.

On the other hand, a macro seems totally reasonable. You’re trying to turn this expression:

julia> dump(:(Trw[i] = 0))
Expr
  head: Symbol =
  args: Array{Any}((2,))
    1: Expr
      head: Symbol ref
      args: Array{Any}((2,))
        1: Symbol Trw
        2: Symbol i
    2: Int64 0

into this one:

julia> dump(:(Trw = setindex(Trw, 0, i)))
Expr
  head: Symbol =
  args: Array{Any}((2,))
    1: Symbol Trw
    2: Expr
      head: Symbol call
      args: Array{Any}((4,))
        1: Symbol setindex
        2: Symbol Trw
        3: Int64 0
        4: Symbol i

You can either do that by hand or use GitHub - FluxML/MacroTools.jl: MacroTools provides a library of tools for working with Julia code and expressions. to make it a bit easier.

1 Like

You can also use Setfield.jl to accomplish this with the @set macro.

using Setfield

a = @SVector [i for i=1:4]

julia> @set a[3]=10
4-element SArray{Tuple{4},Int64,1,4}:
  1
  2
 10
  4

But, in this case, a will be updated?

If you want to update a you need to do a = @set a[3] = 10.

I see, that is what I would like to avoid. I am trying to create a macro that can do this automatically for me.

There was an old version of Setfield, which had this functionality. You could implement it using Setfield as follows:

using Setfield
using Setfield: parse_obj_lens, IndexLens, compose

macro myset(ex)
    @assert ex.head isa Symbol
    @assert length(ex.args) == 2
    @assert ex.head == :(=)
    ref, val = ex.args
    obj, lens = parse_obj_lens(ref)
    val = esc(val)
    quote
        lens = $lens
        $obj = set(lens, $obj, $val)
    end
end

a = (1,2)
@myset a[1] = 10
a

(10, 2)

1 Like