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

Reply via email to