julia> struct Foo
x::Float32
end
julia> foo = Foo(1)
Foo(1.0f0)
But now I realized Float32 isn’t big enough and I want Float64, and I want the change to propagate to the instantiated foo object (not just creating a bigger NewFoo). I believe this isn’t supported.
Parametric types make multiple concrete types, not one type that can be changed in place. For example say I’ve made a struct, instantiated it, moved it around in my program. Now I want to add a new field to the type and to existing instances, in place.
I don’t think there’s easy way to do this. Changing the definition of a type, would require finding every instance of that type (including in boxes and closures, etc) and also recompiling every method that uses it.
If you are just prototyping code, and don’t care that it runs a bit slower, you could do this:
Sorry, my message was meant as a direct reply to @rafael.guerra because to me it seems that package kind of mimics parametric types, or rather when you define a struct in a manner that allows any field type so that you can dispatch on the parametric type.
I’m unsure that the solution proposed by @Per actually solves your problem though:
But now I realized Float32 isn’t big enough and I want Float64
because you wouldn’t be able to change the underlying concrete type of the field, just how it appears “outside” (as he mentions himself).
You should really try the route of parametric types (like @Henrique_Becker said) as in
mutable struct Foo{T}
x::T
end
And then simply recast your data whenever necessary:
a = Foo(Float32(1))
# later
a = Foo(Float64(a.x))
You can even write convenience functions for that and if you need to recast an array of your data, use list comprehensions, etc.
Foo(T::Type,foo::Foo) = Foo(T(foo.x))
# Float32
r = Foo.(Float32.(rand(3)))
# Float64
r = Foo.(Float64,r)
There are probably better ways to do this still.
Edit:
You should probably make the struct mutable because it seems to me that you want to change the contents of the field after the fact.
Structs in Julia are not meant to be used like structs in Matlab for example. Structs are meant to represent user-definable types on which you want to dispatch to do very specific things. That’s why you can’t change their definition afterwards but you can “always” dispatch on parametric types to do more specific things.
If you want a container that has a mutable number of fields, you should use Dicts probably, shouldn’t you?
I think this might an XY problem. Instead of asking “how can I change the definition of a struct in Julia?”, it might be more appropriate to ask “what Julia construct can be used in place of the re-definable structs of [some other language]?” to which the answer is:
This would be a lot easier than the solution that I gave above, and not have any worse performance.
I think dicts have a different layout than structs and different performance characteristics.
The point of this question is about a long-running interactive system where I define a struct as normal, and then change the application without restarting. That is, using struct as intended, and then evolving the application – just without restarting. Consider large interactive Lisp systems to compare.
I’m not a CLOS person but I believe it has this feature.
I think for this very specific example RedefStructs.jl is overkill because you can alleviate the problem of the internally strictly defined type of field x via the use of parametric types.
If I understand RedefStructs.jl correctly (and please, someone correct me if I am wrong), it defines new surrogate structs with the correct type signature with a new name anyway and simply reassigns the name so to you it appears as if you have redefined the struct.
Under the hood what it is doing is equivalent to you writing
struct FooA
x::Float32
end
struct FooB
x::Float64
end
But it additionally updates the name table so that Foo refers to FooB instead of FooA. In this specific case though, you can use parametric types and evade the problem of defining multiple instances of Foo.
Of course, changing the fieldnames or the number of fields in a struct cannot be done after the fact. To reiterate, though, that’s not what structs are meant to do.
If you want to change the application of your programm while it is running, it makes more sense to have defined an appropriate new type and then convert your old type into the new type by, for example, filling fields appropriately.
That way you can still effectively use dispatch.
Edit: Of course, if you very frequently “evolve” your program that might become ineffictive at some point, I guess
Of course this is horrible for performance, but I don’t think take “in place” can mean anything except that a pointer is kept if the type of the field changes. If you need to add more fields of different types, use vectors of union types.
I agree and just want to heavily emphasize again that you really shouldn’t define types like this for performance reasons.
In case it’s necessary, you can restrict parametric types:
mutable struct Foo{T<:AbstractFloat}
x::T
end
Foo(3) # ERROR: MethodError: no method matching Foo(::Int64)
Foo("string") # ERROR: MethodError: no method matching Foo(::String)
Foo(3.0) # Foo{Float64}(3.0)