Maybe you want to join two strings in a list with a space, build a comma separated string from a list of elements or just want to generally understand the options of combine strings in Elixir. This one is for you.
As string manipulation is a big part of most developers it is crucial to understand the options and implications of combining strings. In Elixir we basically have four ways to do that. Interpolation, concatenation, using Enum.join/2
and using IO data
.
We can compare those options from two different perspectives: readability and performance.
Keep in mind:
- Optimize for readability first.
- Optimize performance for significant benefits only.
Lets say we have to elements and want to concatenate them with a space in between:
iex> a = "Hello" iex> b = "World"
Interpolation
Interpolation might be the most readable and simplest to use. It uses to_string/1 in the background to convert every part to a binary.
iex> "#{a} #{b}" "Hello World"
Concatenation
Concatenation is also pretty readable as long as all the parts are strings already. Will raise an error if you try to concatenate non-strings.
iex> a <> " " <> b "Hello World"
Enum.join/2
This is also a very readable way. Comes in handy if you have a list of arbitrary length that you want to separate by space, comma or some other binary. All elements in the list are converted to a binary by using to_string/1.
iex> Enum.join([a, b], " ") "Hello World"
Use IO data for best performance
The simplest, fastest and low-memory-footprint way if you know that it ends up on a IO socket (network or file) Probably the best option regarding the readability AND the performance perspective when you know what you are doing.
Most Elixir functions to write out to an IO socket accept IO Data. See its documentation for more details.
IO data is basically a binary or a nestable list of bytes or binaries.
WARNING: Be sure to convert your elements to binaries upfront. Otherwise they are sent to the IO device as is. Thats the whole point of it!
iex> [a, " ", b] ["Hello", " ", "World"]
The trick here is that no new binary needs to be allocated and not existing binaries needs to be copied. The elements are sent to the network socket or file as is without wasting cpu cycles and memory in between. This is also the main reason why phoenix templates are so lighning fast – apart from being compiled templates.
Summary
While the readability part is mostly up to personal preferences and your coding style there is one method to show significatly better performance than the others. The difference between interpolation, concatenation and Enum.join/2
is negligible. When you know that your binaries end up being written to some IO device you get significant benefits in performance and memory footprint by just returning them as a list.
Read the documentation on IO data for more details.
In short, use interpolation, concatenation or Enum.join/2
unless you know what you are doing but it’s definitely good to know that you have an excellent option to improve your performance significantly in case you need to.
What are your opinions and experiences on this? Any comments, critiques and suggestions are highly welcome.