This is less a meaningful difference than it sounds, because it is also the case that if you write an interface based on Daml-LF 1.x (my option 2), there is no guarantee that it will work against 1.(x+1); by contrast, the spec rules say that LF consumers must assume there are incompatible changes and not even try to parse them.
As a practical matter, when the interface library changes, you are likely to get compiler errors pointing to what you need to change; when the LF protobuf spec adds a minor version and you are interacting with protobuf directly, you will get no errors, only incompatible runtime semantics. Neither is forward-compatible, but the incompatibilities in the interface library are better for your programs’ health.
For example, suppose you have written a LF parser based on the 1.6 spec. You would assume that an external package ref uses package_id_str
. However, in 1.7, this case is never used; instead, you must indirectly look up the package_id_interned_str
. And similarly for module names. Some protobuf gens, like Haskell’s, will warn you about the new cases you need to handle. Some…will not, and you will see the “bug” where package names appear to be present, but empty (because they moved somewhere else). But if you simply upgrade your interface library version, it will handle the de-interning for you.
e: This illustrates the unidirectionality of LF compatibility nicely. There is no way you can “gracefully recover” from the introduction of interning, as an author of an LF 1.6 parser. The only correct thing you could have done at the time was to reject any LF >1.6.
In that respect, I think the interface library actually forms a more stable foundation than protobuf for most LF consumers. With the obvious caveat that you must use Scala or Java to use it.