Partially mutable object

Can you only define some fields in an object constant or immutable, as in C++ class Foo { int x; cont int y; }; ?

One thing you could do is define an immutable struct in which some of the (immutable) fields are wrapped in Refs to make them mutable:

julia> struct Foo
           x :: Ref{Int}
           y :: Int
       end

julia> f = Foo(1, 2)
Foo(Base.RefValue{Int64}(1), 2)

julia> f.x[] += 10  # OK: mutate the Ref
11

julia> f.y += 10
ERROR: setfield! immutable struct of type Foo cannot be changed
Stacktrace:
 [1] setproperty!(x::Foo, f::Symbol, v::Int64)
   @ Base ./Base.jl:34
 [2] top-level scope
   @ REPL[4]:1

In terms of syntax, it does however change the way Ref-wrapped fields are accessed, since you have to put square brackets to access the value itself (f.x[] instead of simply f.x if it were directly an Int).

3 Likes

Note that Ref is an abstract type:

julia> typeof(Ref(1))
Base.RefValue{Int64}

So for performance reasons you might want to use Base.RefValue instead.

5 Likes

The strict answer would be no, you can’t define some fields to be mutable because the concept of mutable types in Julia is different from C++.

But it is possible to make it look like that. One way is already mentioned, make “mutable” fields hold a container, another is to overload setproperty! for a mutable type so that it works only on certain fields:

struct SemiMutable1
    x::Base.RefValue{Int}
    y::Int
end

# to avoid [] for field access
function Base.getproperty(val::SemiMutable1, p::Symbol)
    if p === :x
        return getfield(val, :x)[]
    else
        return getfield(val, p)
    end
end

function Base.setproperty!(val::SemiMutable1, p::Symbol, x)
    if p === :x
        return getfield(val, :x)[] = x
    else
        return setfield!(val, p, x)
    end
end

mutable struct SemiMutable2
    x::Int
    y::Int
end

function Base.setproperty!(val::SemiMutable2, p::Symbol, x)
    if p === :x
        return setfield!(val, :x, x)
    else
        error("field `y` of `SemiMutable2` cannot be changed")
    end
end
2 Likes

Here is a discussion of many alternatives: Mutable scalar in immutable object: the best alternative?

I understand your question, I’m just thinking is it a mistake or anti-pattern to ever do this? So do you have a more concrete example/reason to do this?

If it’s just out of curiosity, and possibly unfamiliarity with Julia (it’s not clear for how long you’ve used Julia), then FYI: structs are immutable (by default) in Julia, ALSO, strictly, in the answers given. I used to like that Julia is an imperative language, i.e. not a pure functional by default.

Maybe @nben’s package can help you:

I can’t recommend Hickey’s talk highly enough on what’s wrong with almost all “information” systems (regarding what he calls place-oriented programming, which implies imperative, almost a synonym, it seems imperative is maybe more general, on how to implement the former).

If it seems long, or you only have a limited amount of time, I recommend giving it a change for even just the first 3-18 min. to see if it’s of interest.

I watched this talk recently, and then immediately again it’s that good, I would say required for all programmers. Note, do not watch it full-screen. I missed out on the slides the first time. That was only part of the reason to watch again, to check if I missed something there. I wouldn’t say it was needed, it was all very clear.

Julia immutable be default in lots of ways, strings, structs, but not arrays/containers, e.g. Dicts. but Air.jl fixes that for all (included in the standard library, in both missing mainly ordered Dicts), the important data structures and arrays

You have a typo cont->const, consider editing your post and doing this way:

julia> mutable struct Foo
           x::Int
           __y::Int
       end

Then x is mutable, more like in C++, while actually __y too (but __ prefix means stay away), i.e. the whole struct. Yes, that’s place-oriented-programming (PLOP)… what you aim for is only half that, a half-sin, and I’m not really sure it’s better. So also consider fully immutable, the default, that you can still use with arrays, but then again you’re back to PLOP. PArray would fix that.