however, I would like to use SVector (in StaticArrays) also. But as I need to mutate the contents of data over time, I need to define another struct which is mutable like:
so, my question is: is there a more beautiful way to do this? maintaining a immutable type along with another mutable type seems uglyā¦ and the behaviors of A and B are basically identical, except for setdata!().
another question is: if I just use (the mutable) B for all cases, would it be stupid because of the performance penalty caused by mutable types?
Can I ask why you donāt want to make A mutable? I mean I guess if data is a Vector{Float32}, at a low level you will have 3(?) pointers to get at the data instead of 2. Not sure what the CPU overhead on that would be, but it should be minuscule.
That said, you might be able to do something like:
struct A{T}
data::T
end
mutable struct B{T}
data::T
end
A(d::T) where T <: SVector = A(B(d))
getdata(a::A{T}) where T = a.data
getdata(a::A{T}) where T <: B = a.data.data
Granted any changes you want to make to the data will need to use the getdata function.
Wait, do you have some instances where you donāt need to update data and thatās why you have two types in the OP? Or will you for sure need to change the data for every instance of A?
If you will always need to change values within data then you donāt want SVector. You may want to use MVector or just a Vector if youāre going to change sizes.
I think I get it. So you want to be able to swap out the SVector but you also want the type to not be mutable because all the other fields stay the same and donāt change.
I think what you want to do is something more like:
struct TypeWithManyFields end
struct SVectorAndFields
fields::TypeWithManyFields
data::SVector
end
Then make a new instance of SVectorAndFields like so:
I want to have a custom type containing a data field which can be a mutable vector (e.g. Vector) or an immutable vector (e.g. SVector).
the whole content of data has to be updated over time. For mutable vector, it could be done by obj.data .= v, i.e. the contents of data are overwritten by those in v. However, for immutable vector, because its content could not be modified, the fielddata has to be replaced like obj.data = v.
now, for obj.data .= v we can use a normal (immutable) struct. But to accommodate obj.data = v, we need a mutable struct instead.
the problem is how to get the best from both worlds (i.e. using a fast, immutable struct for mutable vector & using a mutable struct for immutable vector) without defining two custom types.
or, maybe two types are indeed needed, then the question becomes how to abstract / factor these two types into a single type as their behaviors are basically identical.
Iām still not sure I understand exactly what youāre trying to do. If you want mutable fields then this will allow you to have mutable fields with SVector.
mutable struct MyFields
field1
field2
field3
end
struct MyVectorFields
fields::TypeWithManyFields
data::SVector
end
You said that data will need to be changed for every instance and the fields are going to change too. If they are all changing at the same time then just create a new instance every time they are all changed. In which case this would be fine:
struct MyVectorFields
field1
field2
field3
data::SVector
end
If youāre concerned with the first example because you are changing data a lot more frequently than the rest of your fields and donāt want the overhead of constructing a new instance (which should be small since itās only two fields), then data probably shouldnāt be part of the rest of the data.
Itās difficult to know exactly where the problem lies with any of these solutions without a more concrete example of what you are trying to do.
I think what you just came up with is a nice API. Thatās how Iād do if I were to do this in a self-contained module.
Itās just a nit-picking, but I think the API
is somewhat non-conventional. Usually in Julia, for API f!(dest, args...), I think youād expect dest to be guaranteed to be mutated. However, your setdata! sometimes donāt. I think itās OK for an internal function. Base has something like this; e.g., grow_to!. But if you are going to expose it as a public API, maybe itās confusing?
(BTW, thatās why Iām using a strange !! suffix in BangBang.jl. But I donāt think itās a conventional suffix.)
So do you think itās OK to add APIs like push!(::Tuple, _) and delete!(::NamedTuple, ::Symbol) in Base? What about push!(::StaticVector, _)? I personally find them strange but if Iām a minority Iām happy to use such APIs as they are very useful.
My point is very simple: all I am saying that if the method actually does what it promises, whether it mutates a given argument is irrelevant (as long as that wasnāt promised). Eg if the recommended way to use a method is
then the method could have result === result2 and overwrite it (eg for Array) or make a new one (SArray). As long as this is only conditional on the type, the compiler can deal with it just fine.
So you are just saying that when ādo nothingā is the correct answer, the caller should expect the argument is not mutated (e.g., sort!([1, 2, 3])). I agree, of course. I guess you missed my initial point. It was that itās not possible to define (say) push! semantics on immutable corrections like tuples; hence the weird suffix.
To be fair, I think you are missing my point too: all I am saying is that my interpretation is that !allows a specific argument to be mutated, but does not require it per se.
For push!, the requirement for mutating the collection comes from the fact that it should contain item.