On 28/02/2013 9:43 AM, Erick Tryzelaar wrote:
> Hello Tommy,
>
> I feel our process will ultimately be more along the lines of a small
> standard library that needs hooks into the compiler and runtime, and an
> extensive CPAN-esque repository for everything else. We'll have first
> party "blessed" packages for things like a web client, compression, and
> hashing algorithms.
This "blessed packages" approach to libstd is appealing in some ways but
unappealing in others; it's something we discussed at the last weekly
meeting and I'm curious to hear more perspectives on it. There are a
bunch of tensions at work:
- Terminology-wise, "standard library" as in "gets written into a
standards document" suggests a very minimal libcore-like (or
smaller) component. This is Stroustrup's philosophy[1] and it has
a credible basis. You don't want to burden ISO translators with
writing up the docs to your space invaders emulator[2].
- In terms of user convenience, tracking numerous library versions
is a pain, as is tracking numerous "works on OS $foo" statuses.
A standard-ish (or "common" if we want to avoid the word "standard")
library is helpful in setting expectations about what a user can
expect to be able to get working "on all reasonable installs",
as well as which APIs will have long-term support and high levels
of QA, documentation, integration testing, porting, etc.
- Gating releases of a language on readiness-status of libraries and
maintaining legacy APIs is a pain, true, but users often care much
more about library APIs than they do about language features. "How
do I write $foo in Rust" is very often an API question, not a
language question, and the absence of a good API is equivalent
to saying "go away".
- Certain code idioms are mutually recursive. These need to be in the
same crate for our purposes.
In general my tendency is towards "batteries included" from the _user
experience_ perspective when possible; this does leave some question
about whether (and how) to approach a best-of-both-worlds situation as
far as making the _developer experience_ also pleasant.
I've been pushing around ideas about how to manage possibly re-exporting
pinned versions of particular external libraries as part of the libstd
namespace, as a way to mediate these tensions. Doing so would have
possibly-weird, possibly-ok effects:
- We might have to teach rustpkg to handle the idea of a mod
directive resolving against a pkgid, the same way we're going
to be teaching it to resolve extern mod directives. That is, we
might need to be able to write 'mod foo = "github.com/user/foo";'
and have it imply that the package manager check out that
library but _build it into the referencing crate_ rather than as
an extern. But I think that's ... possibly ok, and might even be
pleasantly uniform in practice.
- The libstd major-version might advance as the _maximum_ of advances
in included libraries. It might bump with nearly every release,
though I have a long-standing idea in mind to mitigate this: version
attributes on module items. This is essentially a way to turn what
_would_ be a major-version bump (API change) into a minor-version
bump (API addition), since the items wind up qualified by separate
version numbers. This might wind up weird or it might wind up quite
practical. We haven't implemented it yet. Put concretely, we might
get away with doing this (in libstd):
#[vers="1.0.0"]
pub mod foo = "github.com/user/foo#1.0";
#[vers="1.1.0"]
pub mod foo = "github.com/user/foo#2.0";
which would "absorb" the major-version change between two versions
of libfoo into a minor-version change to libstd, because we're
"keeping the old API" of foo around qualified with std vers 1.0.0.
Anyone who bound to "pkg.rust-lang.org/std#1.0.0" would get the
old libfoo API they are expecting, but the package manager would
be willing to provide it by downloading 1.0.0 or 1.1.0; in semver
logic we literally are just "adding API" so are treated as such by
version-reasoning tools. Moreover if a user wants to use a mixture
of old and new APIs they can:
extern mod std = "pkg.rust-lang.org/std#1.0.0";
std::foo::bar(1); // using foo::bar 1.0 API
mod futuristic {
// This module's been upgraded.
extern mod std = "pkg.rust-lang.org/std#1.1.0";
std::foo::bar(1, true); // using foo::bar 2.0 API
}
of course if we got sick of maintaining foo 1.0 in libstd, we'd
remove it and that would cause a major-version bump to libstd.
Presumably we'd clear out a bunch of old re-exports at once in
such a case -- anything that we're convinced is obsolete & unused,
or causing undue maintenance pain.
I'm interested to hear thoughts from others, as usual. This is tricky to
get just right but it's important. API compatibility is the thing
hardest to handle over time as they'll _always_ evolve, no matter how
stable the language; having a strategy for thinking about backward
compatibility might help us avoid the version tarpit that has happened
to so many languages (inability to ship new APIs due to mismatches in
users' willingness to upgrade, essentially). How we do this will have
pretty major implications for how we work over time.
-Graydon
[1] https://en.wikipedia.org/wiki/Standard_library#Philosophies
[2]
http://docs.factorcode.org/content/article-handbook-library-reference.html
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev