> That's more like an implementation detail than a quality of the language itself.
It is not, though, because - unlike JavaScript - the fact that everything is a reference to object, and each object has a unique identity, is explicitly a part of Python semantics, and this is very visible in many cases. It's easy to observe it for "primitive" types as well simply by inheriting from them.
(OTOH the fact that a given Python implementation might still implement this by using tagged pointers etc is an implementation detail, because it is still required to behave as-if everything was an object).
> Tagged unions would be, except they aren't first class in C.
For the originally stated example, 'declare a variable, set it to 5 (number), and then set it to the "hello" (string)', a plain union is just fine.
> The best analogy here would probably be OCaml polymorphic variants
It would be, except OCaml is not C.
> It is not, though, because - unlike JavaScript - the fact that everything is a reference to object
Evidently, references and pointers are not the same thing, hence it is an implementation detail whether references correspond to pointers.
> and each object has a unique identity
Which doesn't necessarily have anything to do with their address. It's an opaque value guaranteed to be unique for an object for the duration of its lifetime.
Meanwhile, C pointer values aren't necessarily unique for different objects. For example, a pointer to a struct with one or more fields shares value with a pointer to its first field. It's only once you factor in type that pointers uniquely identify a particular object, so they don't exactly correspond to Python's object identity.
Tagged unions would be, except they aren't first class in C.
The best analogy here would probably be OCaml polymorphic variants (https://dev.realworldocaml.org/variants.html)
> That's more like an implementation detail than a quality of the language itself.
It is not, though, because - unlike JavaScript - the fact that everything is a reference to object, and each object has a unique identity, is explicitly a part of Python semantics, and this is very visible in many cases. It's easy to observe it for "primitive" types as well simply by inheriting from them.
(OTOH the fact that a given Python implementation might still implement this by using tagged pointers etc is an implementation detail, because it is still required to behave as-if everything was an object).