| I wouldn't recommend following the Haskell approach. It hasn't worked well for us. (I took part in creating the Haskell Platform and the process used to add packages to it. I also used to maintain a few of our core libraries, like our containers packages and networking). Small vs large standard library: A small standard library with most functionality in independent, community-maintained packages has given us API friction as types, traits (type classes), etc are hard to coordinate across maintainers and separate package release cycles. We ended up with lots of uncomfortable conversions at API boundaries. Here's a number of examples of problems we currently have: - Conversions between our 5(!) string types are very common. - Standard library I/O modules cannot use new, de-facto standard string types (i.e. `Text` and `ByteString`) defined outside it because of dependency cycle. - Standard library cannot use containers, other than lists, for the same reason. - No standard traits for containers, like maps and sets, as those are defined outside the standard library. Result is that code is written against one concrete implementation. - Newtype wrapping to avoid orphan instances. Having traits defined in packages other than the standard library makes it harder to write non-orphan instances. - It's too difficult to make larger changes as we cannot atomically update all the packages at once. Thus such changes don't happen. Empirically, languages that have large standard libraries (e.g. Java, Python, Go) seem to do better than their competitors. |
> - Conversions between our 5(!) string types are very common.
> - Standard library I/O modules cannot use new, de-facto standard string types (i.e. `Text` and `ByteString`) defined outside it because of dependency cycle.
We have one string type defined in std, and nobody is defining new ones (modulo special cases for legacy encodings which would not be worth polluting the default string type with).
> - Standard library cannot use containers, other than lists, for the same reason.
> - No standard traits for containers, like maps and sets, as those are defined outside the standard library. Result is that code is written against one concrete implementation.
Hash maps and trees are in the standard library already. Everyone uses them.
> - Newtype wrapping to avoid orphan instances. Having traits defined in packages other than the standard library makes it harder to write non-orphan instances.
This is true, but this hasn't been much of a problem in Rust thus far.
> - It's too difficult to make larger changes as we cannot atomically update all the packages at once. Thus such changes don't happen.
That only matters if you're breaking public APIs, right? That seems orthogonal to the small-versus-large-standard-library debate. Even if you have a large standard library, if you promised it's stable you still can't break APIs.