Informal discussion about the progression of `base`

But you can already write “one-off scripts with no .cabal file”, right?

Yes: https://cabal.readthedocs.io/en/stable/getting-started.html#run-a-single-file-haskell-script

Huh, yeah, I guess you’re right. I’m pretty sure I had seen that before, but didn’t remember it. That’s definitely better than not having the option. It’s still not particularly discoverable, so it’s pretty bad for beginners: they’d need to add some special boilerplate just to have access to basic types.

But, to @tomjaguarpaw’s point, we could address that by using the same mechanism to provide more packages by default rather than absolutely needing to merge everything into base.

1 Like

While I think of it: given how special base is to GHC, and that it’s non-upgradeable, put me down for being in favour of as small a base as possible, and better user education around common libraries so that depending on common packages like text and containers doesn’t seem scary to them.

4 Likes

I wish we had vector in base, it is more commonplace and better than Array in base yet Array still has the special status. Vector in base with appropriate special treatment would be great. (The only reason I would use OverloadedLists extension is just for vector, same as OverloadedString for text)

2 Likes

2 posts were split to a new topic: Using more packages by default

This is getting off topic - my main concern is with the base library. Project customization comes in at a later step.

What I want to know is: which direction is most favoured by the community in terms of the base API? Should we invest the effort into merging base and text? If we do, I’d want to do more than just a glorified re-export. I want to see Read/Show using Text. I want fromString to become fromText, and I’d even go as far as making Text the default type of string literals, with my magic wand. This is the sense I’ve been getting from this thread: String was a mistake, and we should invest in fixing it, even if it means – heaven forbid! – straying yet further from the ancient Report.

4 Likes

That’s fair, though I think what has come to light is that everyone has good diverging ideas on things that should be included by default in base, and that to fulfill that using more packages by default seems like the right solution (at least I and @tomjaguarpaw lean towards it), more so than progression in base.

(Except for text, which would have far reaching consequences when merged into base and preferred over String which seems to generally agreed upon here)

Could a mod could split the above “idea” comment over to its own post? A separate discussion does seem better to discuss that concrete proposal, and whether it should be acted upon.

1 Like

I prefer to think of String (and for that matter, the ol’ monomorphism restriction) more as temporary “scaffolding” - there were good reasons in the past for having them in order to get Haskell "up and running". But now each have served their purpose, so it’s probably time to remove them.

Haskell 2010 retired the monomorphism restriction (n+k) patterns; perhaps e.g. Haskell 2024 can just retire String in favour of Text - as you eluded to, that one change by itself would be quite substantial:

  • I want to see Read/Show using Text

    Agreed.

  • I want fromString to become fromText

    …along with replacements for getStr, the putStrs (and yes; even the likes of error - no “scope creep” please!)

  • […] I’d even go as far as making Text the default type of string literals

    Definitely YES! That could make the first step in switching to Text as simple as:

    sed 's/\<String\>/Text/g'
    

    …as opposed to trying to prefix many and varied type signatures with Str (like Num) IsString.


So much for memory! I thought H2010 also dropped the MR - perhaps I was looking at a draft version? ;-/

split base

3 Likes

Honestly, I think something like this may be a way forward, for one critical reason - it sidesteps the issue of asking the conflicting questions of whether things should be added (base is too small) or things should be removed (base is too big), in favor of a way that acknowledges both.

Breaking base up into smaller components and then using flags (or some other method) to control and conditionally prune or include components would allow for a chosen set of default components to form the canonical base, while also allowing for default components to be pruned out, and optional components to be included, making it easier to experiment with changes and for base to be extended.

Then base can be published much as-is, plus a minimal base-slim and a maximal base-all, and anything more specific than this is honestly probably worth doing yourself.

2 Likes

This is exactly why I mentioned splitting base above (and later on, in private conversation with @Kleidukos) as a potential solution to these problems. I’m really hopeful that it would allow something like this to be achieved.

2 Likes
`PartialEq` & `PartialOrd`, superclasses of `Eq` & `Ord`.

At that point we might as well have TotalEq & TotalOrd, superclasses of Eq & Ord.

Coming from Rust, I miss the From and TryFrom traits.
The witch package provides a direct equivalent, so I usually start from Relude and add this on top of it.

Coupled with ViewPatterns it lets you write polymorphic code in such a convenient way:

doThingWithTimestamp :: From t UTCTime => t -> SomeRecord
doThingWithTimestamp (into -> utcTime) = _

I wish we had a standardized way to perform conversions like the Rust ecosystem has.

2 Likes

I personally dislike these sorts of typeclasses; my experience is that an instance basically just means that there exists at least one function from A to B, with no real assertion about its properties, and that’s a really weak condition. It’s particularly bad between string types in a team environment, where someone sets up a ToText class meaning “a lossless conversion exists” and then people add in instances which pretty-print or render in some other way. IMHO, the “what type is actually being passed in here?” experience is real, frequent, and often unpleasant.

If you write it out without the typeclass, it’s the same as doThingWithTimestamp :: (t -> UTCTime) -> t -> SomeRecord, so I usually cut to the chase and write UTCTime -> SomeRecord instead.

5 Likes

Changing topic:

I’d also like to see the symbolic functor combinators defined in GHC.Generics moved into Data.Functor.*, and become the canonical names. Then Data.Functor.Sum.Sum, Data.Functor.Product.Product, and Data.Functor.Compose.Compose can all be deprecated and removed.

3 Likes

I do not have any gripes with base.

I sometimes have to look up what version of base is a boot library of what version of GHC - it would be nice if that table was part of the Haddock documentation of base. (EDIT: I usually refer to either version history · Wiki · Glasgow Haskell Compiler / GHC · GitLab or https://wiki.haskell.org/Base_package.)

The Stack project chooses to use {-# LANGUAGE NoImplicitPrelude #-} and import RIO from rio, but I don’t view that as a deep criticism of Prelude. That said, the README for rio, explaining its motivation, characterises partial functions and lazy I/O as ‘gotchas’ to be removed from a useful prelude.

This FP Complete tutorial on string types advises that using type String = [Char] to represent textual data should be avoided ‘whenever possible’, replacing instead with (strict) Text - but observes that String is the only string-like type defined in base.

2 Likes

You can get exactly that table with ghcup list!

6 Likes

That’s amazing, I never knew that! I always went to some obscure page on the GitLab. Thanks for the tip, though I think putting it in the documentation is also a good idea; it would just require upkeep :confused:

1 Like

I think it is somewhere in the documentation too (on the GHC Wiki in the GitLab, perhaps?), but I can never remember where, and using ghcup list is just so much easier that I never bother.