I'm not sure the idea of a "beginner" language should be a design goal, and I don't think Haskell being a hard langauge to learn means it's a bad langauge.

Easy as a design goal has led to many languages like AppleScript that try to ape natural language, or to visual languages, and those consistently seem to be bad choices.

Haskell is probably not as hard as many people who haven't made the jump think it is. But it is hard.

> What do you need to write real Haskell? The core of the language is extremely simple. If you understand how to write and use [four items broken out below] then you already have everything you need to write useful programs that solve real-world problems.

The core is admirably simple, but simple doesn't mean easy. Easy to learn is when you have intuition and existing skill that readily map to the core constructs used in a language.

> pattern matching

There are cases where pattern matching is obvious, but this is not how most people have learned to structure a problem.

People aren't naturally good at breaking problems into parts, that's a skill you have to develop over two or three years, which is my guess from observing myself and peers.

> sum and product types

Explicit types are already hard; you're already excluding many people who have trouble making the leap from Javascript to Java.

But Haskell's type system is heavily generic. And its support for container types is not so great. Making them easy was a major reason Perl, Python and Ruby grew quickly, and landed us a ton of horrible code.

To understand how Haskell makes these container types hard, compare the API for Data.Map to the API for java.util.Map[2]. You can ignore Java's rather pathetic facilities for FP entirely because you don't need them.

The Haskell version has functions to do lookup, union, intersection and various predicates. In Java, they're trivial to implement with a loop.

> pure functions and values

The intuitive way to change a thing is... well, to change a thing. I can express it directly in plain language. To express the pure way, I have to describe it as "construct a new value such that all properties are the same except for the changed properties."

I've seen a meme that shows Common Core multiplication in a pedagogical context, and contrasts it with the traditional long-form multiplication. It completely misses what CC is trying to do, but it's useful in showing what people are comfortable with: they grok procedures much better than abstractions.

Functions are the part of math where most people who were okay with algebra nevertheless started tuning out. First-class functions are weirder still. Haskell also mandates currying, and being strongly typed every function gets a confusing type signature.

All that said, keeping track of state changed by mutation is deceptively hard. It's a case where your intuition routinely leads you to build things you can't maintain. But sticking strictly to the claim in the OP, it's easy to learn to mutate state but hard to do.

> how to glue together IO to make side effects

This shows that monads are core to the language, and they have all the difficulties of generic types as well.

Everyone has jumped on how hard monads are already so I won't belabor the point, just to say that except for concurrency / parallelism, I don't think there are any core language features as hard to understand as monads.

(And I'm not sure exactly what the "core language" is, but I think anything in the Prelude is fair game.)

Haskell does make them dramatically easier with "do" notation, but I think you really do need to grok monads for your learning to progress beyond simple examples.

[1]: https://hackage.haskell.org/package/containers-0.6.2.1/docs/...
[2]: https://docs.oracle.com/en/java/javase/12/docs/api/java.base...