List of what Haskell doesn't have?

I don’t think I grasp those three perfectly.

Stability is self descriptive, ✓. Coherence of what? The language itself? I think Haskell is more coherent than say Python and way more than C++? High-end quality on what specifically? Quality: «The condition of being of such and such a sort as distinguished from others», where should we specifically being different from others? What does even “high-end” mean in this context?

2 Likes

That too. We’ve poked a lot of holes into the language and exposed compiler specific implementation details, because they happen to allow interesting new features (DataKinds, PolyKinds, TypeInType, you name it). This is a pretty tough engineering problem, so it’s easy for me to point fingers, but hard to productively provide alternatives. I recognize that. But still.

The other thing is indeed the ecosystem. Haskell has shifted largely towards maintainer dictatorship, sometimes benevolent, sometimes not. Take the amount of alternative preludes as an example. Why do they exist? Because base is mostly a historical accident and the barrier of fixing it too high. So people realized: to get anything done in Haskell, you either need to be maintainer or start forking.

That’s unfortunate, but even harder than engineering problems. And it’s also ok to some degree. But it doesn’t help with coherence.

Let’s start with libraries. Yes sure, we have a set of production ready stuff, especially wrt backends (servant+aeson+conduit). But the average state of libraries on hackage is either PoC or abandoned.

That comes naturally with an experimentation eager community. But it also makes decisions hard when you enter a domain that’s less common. Many things have aged poorly and few people care to fix it, because it somewhat works (like tar, which is not production ready at all, yet used by cabal itself).

We can also continue to discuss the state of haskell tls, which has never seen a professional audit and we’re basically clueless about what type of side-channel attacks are possible in Haskell. There isn’t much research about it. Yet, you see this library used a lot in production. No one seems to question it.

Tooling is another topic. But let’s not go into that now.

5 Likes

:100: very well said.

These types of social/technical problems are definitely harder to address, but not only within the realm of possibility, the return in value is also very high. It takes coordination and people working together, putting aside differences and making compromises.

I have had hopes the HF would end up going in a direction that would help this part of the Haskell Experience.

In the meantime, a few of us are putting in energy to chip away at the iceberg that is "what to do with base". It’s a slow process, but has potential if enough people work together. Obviously, it may also go no-where if enough people with authority or decision making power tell us “no, never”.

1 Like

That’s nothing. The really determined Real Programmer can code Lisp in any language.

Oh yeah, another reason to clone Julia JIT / MDP. Haskell doesn’t have homoiconicity, which is another reason to get interpreted JIT / MDP / Homoiconicity built in via language extensions.

My theory is there are few things that both Rust and Haskell lack.

My dream is someday we get a couple million dollars to make GHC a Rust compiler too, and in the process the core language(s) will be forced to become close to perfect, and we will get some cool new surface syntaxes for free.

I like the challenge of trying to implement an existing (non-terrible) language vs just go straight to trying to design and implement a “best of both worlds” language. Implementing an existing spec, even if it has things ones considers mistakes, forces one to come to turn with unknown-unknowns and other blind spots. I think that is an extremely healthy exercise.

3 Likes

I can think of something we don’t have! We don’t have format strings (where the s" herald means a format string for the formatting type “s”, which uses Show):

logOperation operation time timeLength =
  log $ s"Running {operation} at {time} for {timeLength}"
>>> Running Operation Nukes at Time 10 for TimeLength 50

If “s” formatter uses Show instances; then “p” might use Pretty instances:

logOperation operation time timeLength =
  log $ s"Running {operation} at {time} for {timeLength}"
>>> Running Nukes at 10pm for 50s

This is not a particularly hard change, and I think should be added. While Template Haskell libraries have achieved the same thing, this kind of feature really should be in the core language imo.

2 Likes

Changing base would be more instability, though. It seems like you can either have coherence (constantly refactoring things to “make sense”) or stability (leaving the API compatible and accepting a few warts). I’m not sure how you would be able to have both.

I think this is a natural consequence of having a central repository of libraries which has been around for a while and from which nothing is ever deleted. But I think all of those are positive attributes, even if it makes for a bit of work to separate the wheat from the chaff.

For example, in C/C++, there is no central repository of libraries, so although there are tons of abandoned, incomplete, or low-quality libraries in C/C++, they aren’t as obvious because there is no way to list them all.

Rust has a central repository for libraries, but I think they are less likely to be abandoned just because Rust is a newer language and doesn’t have as much history as Haskell. That said, though, I have encountered low-quality or deprecated libraries in Rust, too.

If you want to weed out abandoned libraries, then using Stackage instead of Hackage would be a step in that direction. (Of course, being on Stackage is no guarantee of quality, but it usually means the package is being actively maintained.)

5 Likes

This is something I miss all the time coming from Python and Rust. Building strings in Haskell is incredibly unwieldy. I find the quasiquoter solutions just slightly not ergonomic enough for the cost of bothering to depend on them.

1 Like

There was a suggestion about adding Perl features to Haskell. Perl is widely acknowledged to be the epitome of text-processing languages - perhaps what Python and Rust provide can also be provided just with some Perl syntax…

1 Like
  log $ s"Running {operation} at {time} for {timeLength}"

Is that really so much better than the alternative below that it’s worth extending the language for?

 log $ "Running " <> s operation <> " at " <> s time <> " for " <> s timelength

Yes :slight_smile: I think it’s really that much better personally, having used Scala. Also in reality it would be:

log $ "Running " <> show operation <> " at " <> show time <> " for " <> show timelength

Just now I wrote such a log statement and I think it’s completely unreadable. This feature would:

  1. Be pervasive through any production codebase
  2. Matches the standard of many other languages
  3. Not be that high an impact on GHC (this is my intuition; not fact - I would be surprised to hear this change would not be localised)

Put into perspective: if the “cases” proposal was accepted (which I think is fine), a format string syntax seems orders of magnitudes more important with similar costs.

3 Likes

Not if you pay the low, low cost of defining and importing a single library function.

The great strength of Haskell is that the language is kept sufficiently simple[1], firstly because, and secondly in order that, solutions to common problems can be implemented at the library level. A syntactic feature like do notation has such a vast scope of use that it pulls far more than its weight. Format strings, on the other hand, yeah they’re convenient, but I really don’t see that they pull their weight.

See also: osa1 - Some arguments against small syntax extensions in GHC

[1] Where by “simple” I mean something like “generic”

3 Likes

Perhaps looking at the Scala docs could motivate a little better than I can:

https://docs.scala-lang.org/overviews/core/string-interpolation.html

Note that the different interpolator heralds are library level. I can’t say for sure how the implementation would look in Haskell yet, but I would imagine it would share the spirit of an instance declaration.

Also, a compelling argument to counter yours is that in the languages that have format strings, they are used ubiquitously _and extensively in my experience. Even though we can achieve a similar pattern in Haskell via quasi quoters, we do not see the same usage. I think this is because the simplicity/readibility barrier to entry is the thing that makes them useful, not because Haskell code is so different from Scala.

I empathize with the feeling that we shouldn’t add small language extensions. For example, I think BlockArguments is overall a bad idea (though I could be convinced). However, I feel strongly that format strings are far past the line when compared to the recently accepted cases proposal.

I think a good comparison is OverloadedStrings. OverloadedStrings probably save fewer characters than a StringInterpolation extension would. I’m genuinely curious: would you also remove OverloadedStrings? The two seem pretty comparable to me.

1 Like

Another interesting one is multi-line strings… I often wish I had those. Pulling in and learning an eccentric TH library never seems worth it to me :confused:

2 Likes

Why don’t you consider Template Haskell as part of the core language? Isn’t a better solution just to fix the shortcomings of Template Haskell?

Edit: In particular, the flaws I’m thinking about are that Template Haskell expressions can perform arbitrary effects at compile time and that you need a lightweight Haskell parser to be able to parse the parts between the { } brackets.

2 Likes

Well I think the difference of a few characters/library selection/module import and subsequent readability make a world of difference:

.cabal:
   format-strings

{-# LANGUAGE TemplateHaskell #-}
import Text.Format
foo = log [s|Running $(operation) at $(time) for $(timeLength)]

versus

.cabal:
  StringInterpolators

foo = log s"Running $(operation) at $(time) for $(timeLength)"

I know it seems silly; but coming from other languages, people seem to enjoy the feature. I can’t think of a single reason why Haskellers would not use the feature if available (I certainly would). I would imagine that if we had support for format strings, Haskellers would use it, just as developers in other languages use their equivalents. Perhaps your codebases do use some TH interpolators that I’m not aware of? I have not seen this pattern out in the Haskell wild, even though I’m aware of 2/3 libraries that have the feature.

Perhaps the s" syntax could become sugar for [s|, and then we add s to the standard library. Doesn’t seem like a bad middle ground…

2 Likes

That change doesn’t seem worth saving two keystrokes to me. I feel like the rest of your concerns could be solved by using an alternative prelude (I don’t know if one that includes these format strings already exists).

Personally, I’m content with writing:

myLog ["Running", show operation, "at", show time, "for", show timeLength]

(where myLog = log . unwords)

Or if I really want to be fancy I would make a separate data type with a pretty printing instance:

data LogMessage = Running Operation Time TimeLength

instance Pretty LogMessage where
  pretty (Running op t tl) = unwords ["Running", pretty op, "at", pretty t, "for", pretty tl]

foo = log (Running operation time timeLength)
2 Likes

Relevant: https://www.reddit.com/r/haskell/comments/i3f3ip/state_of_string_interpolation_in_haskell/

I see @tomjaguarpaw commented there also :wink:

A good point mentioned there is dependency footprint.