[plt-scheme] On PLaneT packages and compatible upgrades
Evening, all.
I just discovered something that may affect other PLaneT package
authors, so I'm sharing it here in the hopes that we can avoid some
nasty package incompatibility issues.
PLaneT has the idea of a "compatible upgrade" to a package. If one
version is a compatible upgrade to another, older version, then the
newer version should function as a drop-in replacement for the older.
We don't, however, provide a precise definition of "compatible
upgrades," in part because it's a hard question. But the conventional
wisdom (as I understand it, at least) has pretty much always been that
adding new functionality to a package is compatible, so long as you
preserve existing behavior.
I discovered this afternoon that this isn't always true. Consider the
following situation:
* I write package foo.plt, which contains the following module:
(module foo mzscheme
(define f (lambda (x) ...))
(provide f))
I release this as version 1.0 of foo.plt.
(In the real world, I'd provide f with a contract. I'm leaving those
out for simplicity, since I don't think they're related to this problem.)
* Another party writes an application (or another PLaneT package;
doesn't matter) that uses foo.plt v1:
(module bar mzscheme
(require (planet "foo.ss" ("cobbe" "foo.plt" 1)))
... f ... ;; f from foo.ss
(define g (lambda (y) ...)))
I (the author of foo.plt) am unaware of bar's existence.
* After bar has been written and is in use, I make some changes to
foo.plt, adding some new functionality:
(module foo mzscheme
(define f (lambda (x) ...)) ;; same as in original version
(define g (lambda (a b) ...))
(provide f g))
This (I think) is a compatible upgrade, so I release it as foo.plt v1.1.
* The other person's code no longer compiles. Module bar fails because
it both defines and imports the identifier g.
So, the upshot is that the notion of a compatible upgrade is more subtle
than we (or at least I) thought.
I'm not real sure how to solve this problem. One strategy is that
PLaneT clients should specify exactly what minor version number they
want, as in
(require (planet "foo.ss" ("cobbe" "foo.plt" 1 (= 0))))
That way, the application authors know that they'll always get the same
version of the library -- they don't get an upgrade unless they
explicitly ask for it, at which point they're presumably able to deal
with the consequences. The drawback, of course, is that PLaneT clients
don't get automatic bugfixes.
Another possibility is to restrict the notion of a compatible interface
to require that compatible upgrades may not add new exports. The chief
drawback here is that it requires the package authors to be very
careful. Even if everyone is well-intentioned, mistakes happen from
time to time, and they can require a lot of effort to fix when they do.
Richard