Informal discussion about the progression of `base`

Yeah, I assumed that was a mere typo. At least to me, Text seems like the obvious replacement for [Char], since both are Unicode-aware: if anything, ByteString would surely be a replacement for [Byte]!

Whoops, thanks, I meant Text (and changed in the original post too).

1 Like

In my opinion base should support very basic randomness, including

  • choice of int from a range
  • choice of float from a range
  • choice from a collection of elements
  • shuffling

The API should be very, very simple, preferably concretely residing in IO (no extra typeclasses) and not concerning itself with advanced features such as determinism, choice of source of randomness, etc. I believe these basics cover the most common use cases, people who want advanced features can use third party libraries.

If it’s not already clear, this desire stems from jealousy of python’s standard library - In python I can spin up a quick one off script for experimentation that makes use of basic randomness with a simple import random. See the random module docs for what it provides.

In Haskell I have to involve stack or cabal, which means setting up a project, or googling how to use stack scripts (I can never remember the exact incantation), a barrier of trivial inconvenience which is usually sufficient to get me to not bother, and to just use python instead. It would be more convenient to be able to just use runhaskell or ghci - the additional convenience would be sufficient to cross the threshold to make me choose Haskell for such one off tasks. It’s worth noting that the inconvenience is significantly greater for beginner Haskell programmers - I have no doubt making access to such basic features as randomness require additional ceremony contributes to Haskell’s reputation as an ivory-tower academic language that’s not practical for Getting Stuff Done.

As an alternative, I wouldn’t mind random library being merged into base, but I consider the API much less discoverable, easy to remember, and easy to use than python’s. It falls into the same trap many Haskell libraries do, of prioritising uncommon cases at the expense of simplicity of common cases. For that reason I’d prefer a redisgned, simpler API.

4 Likes

Random is fine IMO. In haskell that would be exactly like you wish it to be:

import System.Random (randomRIO)

main = do
  x <- randomRIO (69, 420 :: Int)
  print $ x * 2
1 Like

You can actually do this — just run cabal repl -b random to launch GHCi with random accessible. I’ll admit this isn’t exactly well-advertised, though, and the friction is definitely an issue (hence why I suggested random earlier as one of the more important packages which could be merged into base).

I think this is largely the fault of the docs, honestly — reorganising them a bit would do wonders. (As a matter of fact, we were complaining about this just the other day over on the FP Discord server.) Random numbers are naturally trickier in Haskell than in Python, since we want to avoid mutable state, and the API itself probably makes the best tradeoffs possible given this constraint.

2 Likes

The problem is that the authors would have to go and update projects to keep in sync.
While this is arguably a benign change and the result would worth it, it is an unwanted* chore.

Add chants of “Text? What is it good for?” and “Don’t optimize before you measure!” and… here goes nothing.

*It would be done already if it was a wanted one.

I actually knew this! I think the fact that I forgot it was possible is indicative of the problem! I’m glad you agree the friction is an issue - launching these one off scripts should require no thought and no friction. Haskell is a high level, garbage collected language with a REPL, and imo as such should support high level scripting capabilities in a similar niche to python without having to make use of package managers or build tools.

I think the naming isn’t ideal either - to take the example wiz gave, I think randomRIO is a much worse name than python’s equivalents random.range and random.uniform.

I disagree with the premise. In most common use cases, people making use of randomness are fine with impurity and expect it. They understand the nature of randomness. They usually aren’t concerned with reproducibility - if they specifically need that feature, they’ll deliberately seek it out. The API should simply make use of mutable state - that’s why I advocated a default of IO. I think it’s really worth interrogating whether we’re using functional purity out of ideology or just out of habit, or because it’s actually the best tool for the job. In my opinion it’s not the correct tradeoff here.

2 Likes

I’m also in the boat of having smaller packages, but a better story for using them all together by default.

Something like having them all by default in build-depends sounds like a simple yet great idea

4 Likes

And since we’re doing our xmas wishing too, for me it’d be Fix in base. It seems like a widely useful concept that gets redefined over and over across libraries, or implies a dependency in data-fix.

2 Likes

We definitely have a lot of friction that prevents many of our nice features from being used as much as they might. cabal -b/--build-depends is one such.

1 Like

Almost? If the packages were also automatically available to runhaskell/ghci/etc—so that we could always depend on them and use them for one-off scripts with no .cabal file—then it would behave the way I’d want.

At that point, though, it seems like we end up with a standard library that happens to be split over multiple packages under the hood. What’s the advantage there? Backwards compatibility? More flexibility around releases and versioning? (Honestly, I could see just those two being enough as long as we can make it work seamlessly in all our tooling.)

1 Like

I want to rip out lazy IO out of base and possibly replace it with proper streaming.

This might be a pipe (lol) dream, but anyway.

6 Likes

More like a “pipes” dream :wink:

Streaming is a tricky thing because there are so many different designs floating around for a core streaming abstraction, but it also seems critical because streams are such a foundational interface type. Lots of APIs naturally want to be streaming, but today that means needing to buy into one specific streaming library at the expense of the others. Not ideal if you’re trying to write a general-purpose library and minimize your dependencies.

2 Likes

The problem is that the [packages’] authors would have to go and update [their] projects to keep in sync.

That is true, but at least they would have a ready-made patch - it would be an exercise in “lessening friction”, rather that trying for “no friction at all” (another example of seeking “good” over “perfect” :-).

*It would be done already if it was a wanted one.

Other chores like swatting bugs and and keeping up with the Glasgow Haskell Change-a-thon could also distract from switching to Text - there are only some many hours in each day…

1 Like

…(heh) much like That Formerly Known As FRP - I would have raised the same point if someone had suggested adding one of those packages or libraries to base (e.g. to make windowing, GUI, etc tasks easier).

I want to rip out lazy IO out of base […]

Perhaps some “sample patches” to exemplify the alternatives would be of use in your quest: you can start with the hedis package and its use of that dastardly-lazy IO :-D

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.

3 Likes