A rant/suggestion about pos instances and substreams
This is going to be a bit of a rant, but I'll try to keep it constructive. To be clear, this is a rant about *one particular aspect* of Kaitai Struct that I find a bit frustrating. I don't have anything against Kaitai Struct in general - quite the opposite :)

## The rant

Currently, Kaitai Struct's `pos` instances are heavily dependent on the mechanics of IO streams and substreams. When you write a `pos` instance, the `pos` offset is always interpreted relative to an IO stream - either one you explicitly specify using `io`, or the implicit default, which is the current structure's `_io`. I find this strange - most other parts of the KSY language try to be declarative and independent of any particular language or concrete parser implementation. `pos` instances are the complete opposite - to use them, you *have* to understand that the generated parser uses multiple IO (sub)streams, and when exactly it creates new ones or reuses existing ones.

The last part is what I find most frustrating in practice. Substreams are an important concept in advanced specs, but they are very difficult to understand, because they are never explicitly declared anywhere - you don't put `substream: true` on a field or type for example. Instead, substreams are only created implicitly, and the rules are not always obvious. When something has an explicit `size` (or `size-eos`), it gets a substream - except when it's a primitive type. Fixed-size types also don't count by default - for example, if you have a `chunk` type that is always 256 bytes long, then a field with `type: chunk` *doesn't* get a substream. If you want one, you have to explicitly set `size: sizeof<chunk>` on the field, which looks redundant if you don't know the mechanics of substreams.

I'm assuming that the current mechanics of `pos` instances and substreams come from how the KSC-generated parsers are currently implemented. This of course simplifies code generation, but I could see this becoming a problem in the long term. By exposing the implementation details of substreams and *requiring* advanced specs to depend on them, it will be difficult to change the compiler's substream logic in the future without breaking existing specs.

## The suggestion

Here's an alternative idea/concept/suggestion for how `pos` instances and substreams could look in KSY. I have no idea if this is in any way practical - I don't know a lot about how KSC works internally. This is just how in my mind these features would ideally work.

In short, I would completely hide the substream mechanics from the user, at least in the context of `pos` instances. Instead, I would specify all `pos` instances relative to an existing field, and let the compiler figure out how the IO should be performed (where to create a substream, etc.). For example:

```yaml
seq:
  - id: magic
    contents: "POTATOES"
  - id: header
    type: header
  - id: data_area
    size: header.len_data_area
  - id: trailer
    type: trailer
    size-eos: true
instances:
  thing_in_trailer:
    pos: trailer.ofs_thing
    relative-to: trailer
    type: thing
```

This would mean that the instance `thing_in_trailer` would start at `trailer.ofs_thing` bytes after the start of the `trailer` field. In this specific case, it's trivial to translate the code to the current substream-based syntax - `trailer` has an explicit size, so it has its own substream in exactly the place we need:

```yaml
instances:
  thing_in_trailer:
    pos: trailer.ofs_thing
    io: trailer._io
    type: thing
```

However, the advantage of the suggested syntax is that you can also specify fields in `relative-to` that don't have an exactly matching `_io`. For example, you could use it with non-explicitly-sized fields like `header` (which have an `_io`, but it doesn't match the start or size of the field), or with primitive fields like `data_area` (which don't have an `_io` at all).

Of course, this suggested syntax is more complex to implement in the compiler, especially for the cases where you don't already have a perfectly matching substream. Honestly though, I would already be happy if the compiler supported the syntax alone, even if it could only handle fields with a matching substream. It would already be a huge help if the compiler gave proper errors for `relative-to: a_primitive_field` and `relative-to: an_implicitly_sized_field`, rather than giving obscure errors about `_io` not existing, or silently doing the wrong thing (#563).

I hope my rant and suggestions made some sense :) If anything is unclear to you, please ask.