Thanks. I looked at the GVariant page a bunch too.
It seems like the encoding is a steam of {type, value} pairs, where values can contain per-type headers as well.
Protobufs, on the other hand, use {field, wire-type, value} where field is required to have an externally known type to parse value, but wire-type is sufficient to determine the length of value, so you can skip unknown fields (used for backwards compatible protocols). In theory, required fields could omit field and wire-type, but Protobufs deemed it more complexity than justifies the space and performance impact.
Primitive values like integers are totally expected in any such format like this. Their salient feature being that they're of known length. I'm a little more leery about "arrays" or other data structures of variable length which are encoded with a known length. Consider Pascal strings {length, [chars]} vs C strings {[chars], NULL}. The later lends itself much better to streaming protocols, but the former is far simpler to work with when you have a complete dataset.
I ran into this situation with a Protobuf I was designing where the first attempt had a message with a repeated field, but it became obvious that I wanted a begin message, a repeated message of singular fields, and then an end message, to allow a fast-start on the send, which didn't require to know the full data set length up front.
There are, however, situations where you do want the length up front. For example, if you need to allocate space to put things. You can get faster parsing if you know the total message size immediately. In general, however, I don't think it matters all that much with modern languages and hardware.
This is one reason why Clojure has both lists and vectors. Lists are lazy head/tail pairs and (count some-vector) is a constant time operation. Unfortunately, Clojure's reader doesn't seem to offer streaming reads of lists (I may be wrong about this).
The bigger issue with unbounded values is that they are more difficult to work with in most languages. Haskell, Lisps, and other functional languages fair far better than most, but once you start mixing fixed-sized messages with known fields, with variable-sized sequences, you wind up with a situation like {x, [ys], z} where a piece of code wants to look at z before looking at ys. If that tuple is represented as an associative structure {:x 1, :ys [2 3], :z 4} then it's suddenly very confusing that it's an ORDERED map and all sorts of assumptions go out the window.
Even more fundamentally: Source code is a serialized protocol. You write down text and the order of the characters on the page have meaning. Sometimes, that order may be over-specified, but regardless, humans see order and make assumptions from it, even when order doesn't matter.