Erlang has 3 types of strings:
1. the list of integers thing, which you should mostly avoid
2. binary strings (like python2 str or go)
3. iolists: a list or element of either of 1, 2 or 3. This is the nice part – it basically means erlang has ropes. And all the "egress" functions (sending udp or tcp packets or writing to stdout or files or sockets etc.) take iolists. So you can avoid a lot of pointless copying and write text or binary data very efficiently; I know of no other (non-BEAM) language that has similarly good support for this, do you?
See http://www.evanmiller.org/elixir-ram-and-the-template-of-doo... for more details.
It is nice in a theoretical sense, in the same way Haskell's [Char] is nice, in a practical sense it's awful.
> 2. binary strings (like python2 str or go)
AKA not actually strings, I don't remember Erlang having much if any string-manipulation API for working on bytes.
> 3. iolists: a list or element of either of 1, 2 or 3. This is the nice part – it basically means erlang has ropes. And all the "egress" functions (sending udp or tcp packets or writing to stdout or files or sockets etc.) take iolists.
This is only for output, so they're not actually ropes.
> So you can avoid a lot of pointless copying and write text or binary data very efficiently; I know of no other (non-BEAM) language that has similarly good support for this, do you?
Most mutable languages will use buffered output streams and possibly some sort of string sequence (even C has iovecs), so while iolists are a good fit for Erlang they make sense in few other languages.
Haskell's list of Char is awful in both a theoretical and practical sense whereas in erlang's case you can think of it as just a degenerate iolist.
> I don't remember Erlang having much if any string-manipulation API for working on bytes
Elixir is better but erlang certainly has string manipulation functionality for binaries: pattern matching, regular expressions, search, splitting and joining etc.
> This is only for output, so they're not actually ropes.
Not true, lots of non-output functions also accept iolists, e.g. the re module.
Sure, but for most languages older than a few years this ship has sadly long sailed and few would be willing to switch to O(n) indexing.
> It's much better to mess up in not having a clear distinction between bytes and text (annoying, but mostly easy to work around)
Except when it's not and the packages you're trying to use simply do not work and fuck up mightily with non-ascii data.
> then to mess up by having constant-time indexable utf-32 as python3 does
It's funny that just above you praise Erlang which does exactly that. Meanwhile Python 3.3 introduced a flexible string representation so depending on the content of the string the internal string encoding may be ISO-8859-1, UCS2 or UCS4 (and yes sadly only these as the designers refused to break the constant-time indexing guarantee).
What the fuck. And I thought constant-time indexable strings were a bad idea!
* Rust has a very strict split between bytes and text
* Go has fucked up bytestrings which depending on validity may break at any point depending on the API you're applying
* Erlang's strings are widely considered awful, and are literally just linked lists of integers making them slow, memory-heavy and prone to pretty-printer whims (as the pretty printer decides on the fly whether a given list looks like a string — and should be printed thus — or does not — and should be printed as a regular list of integers)