4 ms·
This is more for people looking at erlang/elixir than a critique of the blogpost or a suggestion for a change. > Within Elixir, there is no operator overloadin
by IanCal 10y ago
This is more for people looking at erlang/elixir than a critique of the blogpost or a suggestion for a change.
> Within Elixir, there is no operator overloading, which can seem confusing at first if you want to use a + to concatenate two strings. In Elixir you would use <> instead.
When this popped up, it reminded me of something people try to do often and then have issues with performance. You probably do not want to concatenate strings.
"Yes I do" you'll first think, but actually erlang has a neat commonly used thing to help here.
Let's say you're doing some templating on a web-page. You want to return "Welcome back username!". First pass (a while since I wrote erlang so forgive syntax errors):
welcome(Username) ->
"Welcome back " ++ Username ++ "!"
Now it's going to have to construct each string, then create a new string with all three. More creation & copying means things get slower.
Instead, many of the functions you'd use to write files or return things over a connection will let you pass in a list of strings instead.
welcome(Username) ->
["Welcome back ", Username, "!"]
Now it's not copying things, which is good. But then we want to put the welcome message into another block with their unread messages.
full_greeting(Username) ->
welcome(Username) ++ unread_messages()
More appending than is good here, concatenating lists is going to take time. Of course, we could put it all in one function, but then we lose re-usability in the templates and have horrible massive functions. While this is a simple example, I hope you can picture a larger case where you'd want to split up the various sections.
Anyway, there's a better way of doing this. The functions that take lists of strings actually take lists of strings or other lists. So we can just do this:
full_greeting(Username) ->
[welcome(Username), unread_messages()]
You can keep going, nesting this as much as you want. This saves a lot of copying, allows you to split things up and avoids having to keep flattening a structure.
So, for people about to get started, try not to concatenate your strings, you can probably save yourself and your computer some time.
For more info on this, you want to search for "IO Lists" or "Deep IO Lists".
- proaralyst 10y agoThis is a similar idea to a rope: https://en.wikipedia.org/wiki/Rope_(data_structure) https://en.wikipedia.org/wiki/Rope_(data_structure)
- masklinn 10y agoThat's pretty much what iolists are, except you don't usually read iolists (or modify in ways other than concatenating) so you don't pay for that complexity cost. iolists are also mixed-type so you can have an iolist containing integers, strings and binaries (at the same nesting level or different ones) which means less transcoding pressure.
- keymone 10y agoThis is a great advice. Erlang was the first functional language i really learned and i can definitely attest to the pains mainstream developers go through when switching the paradigm. In fact with Erlang you're jumping two paradigms at once - functional/immutable and actor model of concurrency. Eventually it clicked and i think learning Erlang was single most impactful thing on my way to becoming "a better developer". Throw away your best practices of procedural/oop world and unlearn your optimization tricks when diving into functional/immutable. It's a totally different way of thinking about problems.
- coldtea 10y ago>Now it's going to have to construct each string, then create a new string with all three. More creation & copying means things get slower. There's nothing about the ++ syntax that makes it necessarily so. The compiler/interpreter could understand that we're getting a concatenated string and automatically only create one. Which is the case in several languages.
- filmor 10y agoWhat is different in Erlang is that strings are a bit messed up in that they are linked lists of numbers. What you are suggesting (constructing the actual string in-place) works fine in Erlang, too, but has a bit of a funky syntax: welcome_user(Username) -> << <<"Welcome ">>/binary, Username/binary, <<"!">>/binary >>. However, the main advantage of using io_lists instead is that `["Welcome", Username, "!"]` has constant complexity with regard to the length of `Username`.
- diamondlovesyou 10y agoNote to anyone not already familiar: in Elixir, character strings are all binaries from the start. Just to avoid the notion that Elixir follows the mistake of strings-are-linked-lists from Erlang.
- IanCal 10y agoThis could be true in some restricted cases, but can't be in general. If the username came from the database, it's going to be stored in memory before this function is called, and so concatenating must require building a new string.
- coldtea 10y agoIt can require building a new string. But what is argued is that it wont require building several new strings incrementally for each concatenation. E.g. no: A + B + C + D -> AB + C + D -> ABC + D -> ABCD And for immutable strings, it might not even require to build a new string at all, even if the string is in memory: it can just use a string data structure pointing to the various parts. (
- esra 10y agoJust be aware that there are libraries that don't work with io lists, like erlang's http client httpc, in that case you need to flatten the io list.