Informal discussion about the progression of `base`

The Haskell community has struggled with its base library since time immemorial (1), as evidenced by the number of alternative Preludes, the (modern) lack of alternative compilers, and recent comments in CLC issues.

I’d like to get a broader view of the community perspective on the base library. I want you, Haskeller! to tell me its flaws, no matter how big or small, that you consistently bump into. I want to hear your personal gripes with particularities of its structure, whether you’re a new or seasoned Haskeller. I want to know your opinion on the consistency and effectiveness of the implementations and their names. I want you to vent, here, all the problems you have with it, what you like about it, and what you think we can do to make it better, and I want you to be heard, without forcing you to do a bunch of work to write a proposal.

Please, unleash your inner pedant, for Great Good!

(1) Literally since before I was born.

6 Likes

Overall, I don’t think about base much. It’s fine, I can make it do stuff, I don’t usually get bitten by version upgrades. If you gave me a magic wand, I’d wish for the following unrealistic changes:

  • I’d love to see Num split up:
    • Move fromInteger into its own class so that you can have (e.g.) libraries which can turn an integer literal into a HTTP status.
    • Make (+) the method of class Semigroup. Similarly move (*) into some other class: Semiring may be too strong (must we require commutative (+)?)
    • Make (-) a method of class Group, I guess?
  • Make map the method of class Functor
  • Rename mconcat to concat

I used to be pro-Profunctor-in-base to make it easier to write van Laarhoven Isos, but I think a small base is better than a big base. More generally, I’d like to see how Iceland_Jack’s FunctorOf work pans out before baking in more of our current ___Functor classes.

I’d also like to see some kind of smaller, teaching-style alternative to base that educational material could depend upon.

4 Likes

I would like to see Data.Text in base.

Another common complaint is GHC/base coupling.

A third one is to specify better Eq requirements.

8 Likes

I’d go further: I’d really like to see all of containers, array, bytestring, etc. in base. A standard library with no good data structures is a bit of an embarrassment. I am aware that there are maintainability issues around doing this, but hopefully the plans around splitting base might eventually make this a bit more feasible.

(And yes, I do also know that ‘standard library’ is a bit of an ambiguous concept in Haskell, since it could equally well apply to the collection of all the boot libraries. But, amongst other things, base is the first library learners see, and the only library included by default in Cabal projects.)

8 Likes

In particular, I would like to see concepts like Indexed included into base (and ditto Biindexed for Bifunctor).

Several commonly-used modules have functions specific to their data structures, such as Map.mapWithKey and Vector.imap, and there exists a disparate set of classes in lens, indexed-traversable, keys, mono-traversable-keys, but they are un-unified, which can be an impediment to their use. If brought into base, their implementation and use could be made consistent for better integration (eg, the same reason for having Functor be a superclass of Bifunctor), instead of having a scattered set of unrelated modules.

I personally would like to see them implemented in the following manner:

type family Index (f :: * -> *) :: *

class Indexed f where
    indexed :: f a -> f (Index f, a)
    
class (Indexed f, Functor f) => IndexedFunctor f where
    imap :: (Index f -> a -> b) -> f a -> f b
    
class (Indexed f, Foldable f) => IndexedFoldable f where
    ifoldMap :: (Monoid m) => (Index f -> a -> m) -> f a -> m
    
-- NOTE: Or just (Indexed t, Traversable t) => ...
class (IndexedFunctor t, IndexedFoldable t, Traversable t) => IndexedTraversable t where
    itraverse :: (Applicative f) => (Index t -> a -> f b) -> t a -> f (t b) 

I would also like to see the concepts from recursion-schemes (and Fix Free and Cofree) pulled into base as well - I have less justification for this other than that recursion is a really important concept, and I am always annoyed when I remember that they aren’t already in base and so I am forever pulling them in.

However, I also see recursion as an accompaniment to the whole Category, Monoid, Monad triplet, for reasons that take a bit more explaining so I’ll just gesticulate wildly at how deeply Turing-completeness and computational complexity are related to it in general, and wonder how it could possibly be missing from base.

I’m biased, because I work with both of these a lot.

Edited to add more context, and then a tad more.

For those who have said they want more stuff in base can I ask them to clarify what exactly that would improve?

2 Likes

Lack of build-depends: xyz. For a datatype that is considered blessed by the community (as Data.Text) that means a lot for adoption, future APIs using it.

5 Likes

So having to put text in the build-depends is a significant hurdle to adoption?

I think it’s more that the inertia of String is simply stronger when there are any hurdles to using Text. Text in base means base APIs that speak Text instead of String, for instance, and this has knock-on effects for the rest of the ecosystem.

10 Likes

Ah yes, with text in base all the filesystem APIs could use Text, Show/Read and so on.

6 Likes

There are more alternative takes to the numeric tower than proponents. One of the most coherent ones is NumHask
NumHask , which nonetheless adds a lot of compilation overhead .

that was what base was supposed to be initially… but times indeed have changed.

1 Like

Yes, I too would like a “fat” standard library, similarly to what Python offers (The Python Standard Library — Python 3.11.4 documentation). Not claiming that Python stdlib is perfect either, but you do get a lot of mileage out of it. containers, text, aeson and bytestring are something I literally import all the time (if I were allowed to dream I’d add transformers, mtl, unliftio, stm, …!) . Not to mention AST reification/reflection like ast — Abstract Syntax Trees — Python 3.11.4 documentation . Granted, we have a more complicated compilation story but providing some of the ghc API and perhaps even template-haskell would be nice?

2 Likes

What’s the benefit of a “fat” standard library over just depending on all those “thin” packages? Say cabal automatically brought containers, text, aeson and bytestring into your dependencies (and you had be explicit to “undepend” on them). What would be the difference to a “fat” standard library?

3 Likes

For one thing, the cognitive load of having to remember where everything is…

Regardless, this isn’t a package manager’s problem, it’s a language problem. I personally quite liked Relude’s approach of re-exporting the main container and string types, but even then, I had to import them explicitly in my cabal file to get HLS to recognize anything poignant about actually using them.

3 Likes

That everything is in scope right away like String, IO etc?

Oh, so you want Text in the Prelude, not just text in base? That makes perfect sense. Just trying to clearly understand exactly what people are asking for here.

2 Likes

Can we clearly answer:

  • why does base need to change?
  • what needs to be changed?
  • how this change is supposed to happen? What is the deprecation story, how can we prevent existing code from breaking abruptly?

I care deeply about not breaking code with each GHC release. As such decoupling base from GHC releases might be worthwhile. Then again this is mostly about stable a API, and no need for ecosystem churn for each compiler release.

5 Likes

Why does base need to change:
There seems to be an over emphasis on completeness over correctness, and/or reliance on a machine word sizes that change depending on the machine.

What needs to be changed:
toRational for Double produces non-sense values for NaN, and +/-Inf.
The existence of toEnum and fromEnum that produce non-sense for values larger than Int, which may be different depending on what machine you run it on.

Deprecation story:
There are already some documentation about these issues, but…
Implement a warning, then let everyone who depends on bad concepts fix their code.

And probably other similar things…

I think base is probably already too big, but I’d really like to see Fix and Comonad become a part of it. I’m actually a bit surprised that they’re not part of base, but things like Compose and Bifoldable1 are.

1 Like

Just to clarify, when I mean deprecation, I do not mean some document somewhere saying something should not be used. Any deprecation that is *not a compiler warning for at least 2 GHC releases (while base is tied to GHC) virtually does not exist.

3 Likes