I've never personally understood the exaggeration people use when talking about string types in Haskell. You say "zillions", but there's only really five. Five is the most disappointing "zillions" I've ever encountered.
There's strict and lazy ByteString, which you should use whenever you're working with buffers of bytes that don't have any associated encoding and aren't text.
There's strict and lazy Text, which you use for human-readable text that's decoded from some specific encoding, like ASCII or Unicode.
There's String, which you use when interacting with an API that requires use of String, like anything in the language standard.
It's certainly some amount of complexity, but it's not anywhere near as complex as I keep seeing claimed, unless I'm missing something. It's certainly ugly that we still have to deal with String, and that's a notable wart on the language, but I rarely even think about it.
One guess I've had about part of the confusion is that ByteString has the word "String" in it, which might make people assume that it's for text. A better name would be "Buffer" or "Bytes". I've also speculated that maybe part of the complexity is having to explicitly encode and decode to get Text, instead of the implicit coercion you get in other languages that freely mix bytes and text.
If you want a greatly simplified way to unify string conversion, you can use string-conv. https://hackage.haskell.org/package/string-conv-0.1/docs/Dat...
Edited to add: it's been pointed out to me that "zillions" might not have actually been a claim that there's enough string types actually keep track of them, but is probably just hyperbole to express some frustration. If that's the case, my apologies for my failure at reading comprehension.