Function which returns an immutable type using Metaprogramming


Hi I have a question on defining a function that returns an immutable type.
To be specific, let me define two immutable types:

immutable test1
immutable test2

For t1 = test1(1,1) and t2 = test2(1, true), I want to change t1.a and t2.a from 1 to, say, 2. But since both test1 and test2 are immutable type, I cannot change the value in the usual way.

So I wrote a function as follows:

using Base.Cartesian
@generated function change_immutable(t::Any, name::Symbol, val::Any)
    names = fieldnames(t)
    len = length(names)
        @ncall $len typeof(t) i-> 
             getindex($names, i) == name ? val : getfield(t, i)

The first argument takes any (immutable) type, the second argument takes the symbol in the type where the corresponding value needs to be changed to val, the third argument.
For example, change_immutable(t1, :a, 2) gives test1(2, 1) and change_immutable(t2, :a, 2) gives test2(2, true). I know that t1 and t2 haven’t changed, but that is okay and this function does what I want to implement.

But the problem is that change_immutable function sometimes allocates additional memory. Using the following function:

function memory_test(t)
    for i=1:1000
        change_immutable(t, :a, i)

@time memory_test(t1) returns 0.000013 seconds (4 allocations: 160 bytes) but
@time memory_test(t2) returns 0.000067 seconds (1.49 k allocations: 39.047 KiB), which allocates additional memory compared to the first case.

Do you know how to construct a function that does the same thing as change_immutable yet allocates less memory even in test2 immutable type?


The extra allocation don’t happen on v0.7 so it seems to be some sort of optimization problem in v0.6.

Ideally you would want to directly generate the correct expression T(t.f1,t.f2,t.f3,val,t.f4) but I’m not sure that’s possible.



Yes, I was using v0.6. Great to hear that v0.7 does not have this problem!


This is what I’ve been looking for! Thanks a lot!!!