No one is mentioning that in Elixir/Erlang (and a bunch of other FP langs) a string is implemented as an immutable linked list of integers. linked lists have the property of its nodes being all stored separately in memory, which means we don’t really need to worry about space/time complexity while adding/deleting nodes in the list.
For example, A -> B -> C -> D is a linked list, but so is B -> C -> D, C -> D and so on, and each linked list is just a sub structure of another linked list.
This is interesting, because now we can confidently say that A -> B -> C -> D is a different list from B -> C -> D (even though one recursively contains the other one). Because we have that guarantee (along with the fact that a list CAN'T change), we don't have to define the same data twice, and we can reuse existing linked lists! This is called Structural sharing. Adding to this list as an extremely low memory cost.
If we go back to our html snippets that we are sending down the wire, you can see why all of this would have an advantage over how an imperative lang might represent strings under the hood.
Your explanation of structural sharing makes sense but there's a lot of nuance there:
1. Elixir strings are Erlang binaries (byte arrays), not charlists. Charlists are becoming rare nowadays even in Erlang because they're so memory-inefficient.
2. Comparing two lists for equality requires traversing them, you can't just look at the first node. If they were built independently they won't share structure.
3. HTML templating is (reasonably) efficient because of iodata, not charlists.
Just to add to your explanation, Lisp cons cells works that way as well, and are implemented as linked list.
The great and often unmentioned pro of cons cells/linked lists is that they are a very simple form of persistent data structure, that make immutability and memory sharing very easy, so are an excellent primitive to build a garbage-collected immutable and shared-nothing language upon like Erlang, or Clojure.
For example, A -> B -> C -> D is a linked list, but so is B -> C -> D, C -> D and so on, and each linked list is just a sub structure of another linked list.
This is interesting, because now we can confidently say that A -> B -> C -> D is a different list from B -> C -> D (even though one recursively contains the other one). Because we have that guarantee (along with the fact that a list CAN'T change), we don't have to define the same data twice, and we can reuse existing linked lists! This is called Structural sharing. Adding to this list as an extremely low memory cost.
If we go back to our html snippets that we are sending down the wire, you can see why all of this would have an advantage over how an imperative lang might represent strings under the hood.