The doc section about ismutable
says:
help?> ismutable
search: ismutable ismutabletype isimmutable
ismutable(v) -> Bool
Return true if and only if value v is mutable. See Mutable Composite Types for a
discussion of immutability. Note that this function works on values, so if you
give it a type, it will tell you that a value of DataType is mutable.
Standing on this word: Return true if and only if value v is mutable and also what the doc says here about strings:
As in Java, strings are immutable: the value of an AbstractString object cannot be changed. To construct a different string value, you construct a new string from parts of other strings.
is there a reason for this still returning true
:
julia> ismutable("apple")
true
The above leads to bugs in code where strings are technically known not to be "changeable", but then ismutable
still returns true
. Moreover the ismutabletype
returns true
for strings as well:
julia> ismutabletype(String)
true
I'm beginning to think this behaviour for strings is similar to BigInt
and BigFloat
where same process happens of ismutable
and ismutabletype
returning true
happens also. IMHO, this shouldn't be the case for strings.
While I agree that it would be nice if ismutable
returned false
for strings, my understanding is that ismutable
is not very reliable in production code. For custom types, it seems that ismutable
boils down to whether your type was defined as a struct
or a mutable struct
. Consider this example:
struct A{T} <: AbstractVector{T}
x::Vector{T}
end
Base.size(a::A) = size(a.x)
Base.getindex(a::A, i) = a.x[i]
Base.setindex!(a::A, v, i) = (a.x[i] = v)
julia> a = A([1, 2, 3])
3-element A{Int64}:
1
2
3
julia> a[2] = 100
100
julia> a
3-element A{Int64}:
1
100
3
julia> ismutable(a)
false
So we see that vectors of the custom type A
are mutable, but ismutable
returns false
, because the underlying struct
is immutable. 🤷♂️
So what do we use in production code to determine what's mutable and what isn't?
While I don't really know the design behind all this, I'm beginning to feel the best bet is to use is isbitstype
as stated from its docs:
help?> isbitstype
search: isbitstype
isbitstype(T)
Return true if type T is a "plain data" type, meaning it is immutable and
contains no references to other values, only primitive types and other
isbitstype types. Typical examples are numeric types such as UInt8, Float64, and
Complex{Float64}. This category of types is significant since they are valid as
type parameters, may not track isdefined / isassigned status, and have a defined
layout that is compatible with C.
See also isbits, isprimitivetype, ismutable.
I'm beginning to feel the best bet is to use is
isbitstype
Quote from Keno on Discourse:
Well, the reflection functions answer questions about the system, not convention.
In other words, the reflection functions like ismutable
and isbits
are not really designed for everyday programming, they're designed to answer questions about the underlying objects.
So what do we use in production code to determine what's mutable and what isn't?
If you're only concerned with arrays, you can use ArrayInterface.ismutable
, from ArrayInterface.jl, which is different from Base.ismutable
. However, not all third party packages that implement array types necessarily use ArrayInterface.jl.
Otherwise, it depends on the context. You can add specific dispatches for the (abstract) types that you know should work. Or you can take the "let it fail" approach. In other words, just use mutation in your function, and if the input object is mutable it will work. If it's not mutable it will throw an error. That's generally the approach that generic code has to take, since there is no IsMutable
trait.
I understand perfectly and agreed on what you said until the point of:
You can add specific dispatches for the (abstract) types that you know should work.
Inasmuch as that is cool, it shouldn't be that way, since code will show surprises between different users with same source code. If I dispatch and then teach someone with my code, chances are the worst will happen if I DON'T REMEMBER the dispatch part.
There should be a way. Possibly I'd just stick with the isbitstype
for now since its reasonable enough.
Possibly I'd just stick with the isbitstype for now since its reasonable enough.
For what? What decision are you going to make based on the result of isbitstype
?
With the doc string from isbitstype
I find it reliable than the ismutable
or ismutabletype
:
help?> isbitstype
search: isbitstype
isbitstype(T)
Return true if type T is a "plain data" type, meaning it is immutable and
contains no references to other values, only primitive types and other
isbitstype types. Typical examples are numeric types such as UInt8, Float64, and
Complex{Float64}. This category of types is significant since they are valid as
type parameters, may not track isdefined / isassigned status, and have a defined
layout that is compatible with C.
I recommend opening a thread on Discourse to get feedback on your particular use case.