How to design with immutable and also mutable type?

more help needed… I’m so bad in metaprogramming :dizzy_face: Suppose I wanna build a macro such that:

@Δ(f, obj, x)

expands to:

obj = f(obj, x)

in the “calling” scope, i.e. works fine in REPL or inside a function.

my initial attempt is:

julia> macro Δ(fun, obj, x)
           :($obj = $fun($obj, $x) )
       end
@Δ (macro with 1 method)

julia> @macroexpand @Δ(setdataΔ, v, [3, 4])
:(var"#4#v" = Main.setdataΔ(var"#4#v", [3, 4]))

now there’s a lot of Main., meaning that this macro does not work inside a function… :dizzy_face:

the second attempt use esc:

julia> macro ΔΔ(fun, obj, x)
           :($(esc(obj)) = $(esc(fun))($(esc(obj)), $(esc(x)) ) )
       end
@ΔΔ (macro with 1 method)

julia> @macroexpand @ΔΔ(setdataΔ, v, [3, 4])
:(v = setdataΔ(v, [3, 4]))

now, the Main. disappears… is it the correct implementation? does @ΔΔ works in all calling scope? thanks. :pray:

@tomtom I think @ΔΔ does what you want. FYI an easier way to do this is

julia> macro ΔΔ(fun, obj, x)
           esc(:($obj = $fun($obj, $x)))
       end
@ΔΔ (macro with 1 method)

julia> @macroexpand @ΔΔ(setdataΔ, v, [3, 4])
:(v = setdataΔ(v, [3, 4]))

although esc(expr) is incorrect if you have something that does not come from the argument (e.g., esc(:($obj = setdataΔ($obj, $x))) probably does something bad).

@Tamas_Papp Maybe a better example of what you were pointing out is qr!(A)? The caller allows the callee to modify the input A but it is done in an unspecified way. I think not mutating the input at all is an acceptable implementation. Generalizing it, there are ! functions that may mutate the arguments because they are treated as workspace. I do agree this and my first comment was perhaps generalizing too much. However, I don’t think it’s popular to expose a public API that may or may not mutate “output argument” (e.g., the first argument of push!). At least I don’t remember seeing such API in Julia.

2 Likes