I proposed allowing non-semver, as an option, or at least for debate:
It got me thinking how would you implement this (and keep “full” compatibility), and I have a dilemma.
julia> v"∞" # rarely used option... not even sure is documented, though I think I know when used in obscure cases, i.e. not by most users...
v"∞"
julia> dump(ans)
VersionNumber
major: UInt32 0xffffffff
minor: UInt32 0xffffffff
patch: UInt32 0xffffffff
prerelease: Tuple{} ()
build: Tuple{String}
1: String ""
struct VersionNumber
epoc::UInt8 # plausibly proposed new member...
major::VInt
minor::VInt
patch::VInt
prerelease::VerTuple
build::VerTuple
At a minimum version numbers are stored as 12 bytes (3x larger than needed), plus some overhead for the almost never needed optional parts…, even if those are not used. As you can see the format (sometimes) includes a String, i.e. stored on the heap currently, so requires allocation, and implies GC activity… and heap objects probably take 64+ bytes there, plus the 4-byte pointer (usually, i.e. on 64-bit CPUs).
But my main point is that if I add that 1 byte for epoc (more because of padding, I believe, if not moving it last), then what happens if you (potentially) serialize an object? It’s a different larger struct. I.e. a new struct in newer versions of Julia.
You want to be able to deserialize all objects: This new struct, in new Julia (or for any updated struct in a program in even same Julia version), and, in some or most cases be able to deserialize the old struct, from binary serialized formats made with older Julia. Then this new binary format needs to store the Julia version, and old Julia versions can’t read the format used from newer Julia, even if epoc is in all cases 0 (the default) in the file.
In many, or most(?) formats, e.g. Julia’s Pkg TOML format, the formats are text-based (or some other way of variable-length format avoiding the problem) version numbers are stored as strings, i.e. as part of the larger full text-based format file.
And that’s fully ok. It’s not like version numbers are speed-critical…
Plan B to implement the struct, could be keep the old one with:
major = (epoc << 24) + major
We likely never needed full 4-byte into for the major version number. This limits its max to 16777215 which is hardly limiting, but it IS strictly technically breaking change, in case someone had ever made a larger version number and stored it in a (binary serialized) file…
My preferred solution would (have been) be a “text” format, even in memory, not some JSON or TOML file, in memory, just a plain version string in memory. When it’s constructed, it needs to be validated, but then stored (or rejected). It’s likely fastest for anything needed (at least with my alternative string format that wouldn’t use the heap in most cases, it’s not like the current format never does use the heap anyway).
The downside is that accessing components of the version numbers is slower, because you have to reparse every time, not just access it in a know location. But it’s not for sure that it’s slower, because the current struct format is more verbose than it can be. And because of memory saving, I think the idea can work well for arbitrary structs (maybe not individual numbers, but nobody cares about single numbers).
Comparing version numbers would still be fast for equality, or non-equality checks. Ordering would maybe be slower, maybe not… Printing will be faster. Likely most important operations will be faster, and none of them speed-critical anyway. But extending the format, e.g. adding the epoc prefix is easier, or some hypothetical suffix, or something else.
Currently everything in Julia can be redefined at runtime except structs. There are hacks, including a package to redefine, in the same REPL session, so this is not just about changing across Julia versions, or application versions.
If we have “extensible structs” as a concepts, ALL of them can fit into 4 bytes (fixed-length, with variable-length on the heap an option), because I need a 64-bit pointer as the escape hatch, and with bit-stealing/tagged pointer I can use at least 53-bits for data. That’s basically my new hybrid string idea. But it could be extended to numbers, and all structs. Floats, such as Float64 are all implicitly structs, and most of the time you don’t need 64 bits for numbers. You can e.g. get away with 32 or 16 bits, even for floats WITHOUT accuracy loss.