List of what Haskell doesn't have?

Is there a list of what Haskell still lacks?

In talking with a C++ programmer about Haskell, he remarked that Haskell isn’t a language, it’s a “meta-language”, which is appropriate given that Haskell is a ML dialect.

In reality, despite the reputation of Haskell as being pure, the anti-thesis of OOP, and so on, Haskell in fact has virtually every language feature in either a library or a language extension. For instance, if the question is purity, Haskell has unsafe functions as well as IORef to enable mutability. Dynamic types? There’s a Dynamic library out there. OOP? Check out the Objective library.

However, there are still a few notable features that this particular meta-language still lacks. Here’s a list off the top of my head, are there any more features that are lacking?

-Multiple assignment. Sometimes, when dealing with records, multiple assignment can make life easier. Haskell has multiple assignment when it comes to type signatures, but it doesn’t have it when it comes to records or names. Multiple assignment, when it comes to records, would at least be useful in creating default config records.

-Dependent Types. This is well-known and is something being worked on.

-JIT / Multiple Dispatch Paradigm. We’ve described Julia’s JIT / MDP system within another thread, but Haskell, to the best of my knowledge, lacks JIT / MDP.

-Prototyping via Data record declarations. Within record syntax, sometimes building a prototype then mutating the prototype as needed is useful, but right now, you have to do a Data declaration for the record first, then create a prototype separately, resulting in boilerplate.

Anything else Haskell is missing right now?

3 Likes

Stability, coherence, high-end quality. I know I’m being boring, because those are not “features”.

7 Likes

I think for many of the things you mention that Haskell does have, there is usually a catch. Most of these features are either not very performant or not very easy to use or both. For your examples:

  • I do think we have good support for multiple assignment, e.g. let (x, y) = (1, 2) in ... or do you mean something else?
  • You can write dependently typed programs in Haskell (e.g. with singletons), but it is not quite as performant and easy to use as for example dependently typed programming in Agda.
  • We do have some kind of multiple dispatch using type classes, although that is not guaranteed to be zero-cost like it is in Julia.
  • I don’t know exactly what you mean with prototyping, but I think something like vinyl does cover that use case, but, again, it is probably not quite as performant or easy to use as a native language feature would be.
4 Likes

I mean, say,

bar = Bar {cat, dog = Mammal :: Animal}

Iirc, we don’t have that, right? Or is that still somehow possible to do via Record syntax?

Re: multiple dispatch, getting zero cost is sort of the point; since Julia aggressively uses multiple dispatch to achieve performance as 1-2x C.

Could you explain what that syntax would mean? I don’t understand what you mean by “multiple assignment”.

1 Like

I think it should just mean:

bar = Bar { cat = Mammal :: Animal, dog = Mammal :: Animal }

I think the closes you can get with Haskell today is:

{-# LANGUAGE RecordWildCards #-}
bar = Bar{..} where (cat:dog:_) = repeat (Mammal :: Animal)

That is hardly shorter in this case, but I guess it can be a lot shorter if there are a lot of fields or if the value is larger.

3 Likes

Heaps of stuff that’s in C++ because C++ is imperative and OOP (and low-level), but not in Haskell because it isn’t. There’s no pointers or dereferencing in Haskell, for example. Pointless question.

There’s one of those jokey claims “The determined Real Programmer can write FORTRAN programs in any language.”. You seem determined to write C++ in Haskell. But “There is a hell of a lot more difference between the various programming languages than their syntax. … it won’t be idiomatic …”

So you want to create a record of default values, and then overwrite some of the fields with something more specific? That sounds like imperative thinking with destructive assignment. Not an idiomatic approach in Haskell.

Is valid in GHC today (if you switch on the right extensions). It doesn’t mean what you want it to mean; it’s not idiomatic Haskell:

  • It’s idiomatic Haskell to give a type signature for your top-level names (like bar), not leave them to be guessed by burying annotations in bindings.
  • Mammal is a data constructor: its type is obvious, why is there an annotation?
  • Bar is a data constructor; the types of its fields are given in the data decl, why is there an annotation?

Bar{ cat } is sugar for Bar{ cat = cat } (with -XNamedFieldPuns). So there’ll be a variable cat in scope, bound to some value (perhaps a default). The same could go for dog. Then your code could be

bar = Bar{ cat, dog }

If there’s something you write often in Haskell that seems long-winded and repetitive, you could always propose an extension for some short-hand.

But I’d ask yourself first whether you’re trying to force a design pattern from some other language/other paradigm into Haskell.

I second this, these aspects are imho the ones in need of urgent care.

3 Likes

Quick Googling reveals Foreign.Ptr and Data.IORef for pointers in Haskell.

The focus on my interest is that Haskell has a wide extensions and libraries system, so much so that for virtually any given language feature, someone has implemented it in Haskell.

Whether the given feature is actually useful and belongs in idiomatic Haskell is a whole other question, but this makes Haskell useful for teaching, as it can be used to teach virtually any given feature of another language.

2 Likes

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