Understanding IOData

Hi all

Could someone please explain me what is IOData?



According to the IO docs, iodata is binaries or lists of bytes and binaries. Easy to understand when you try:

iex(1)> IO.iodata_to_binary([65,66])  
iex(2)> IO.iodata_to_binary("AB")     
iex(3)> IO.iodata_to_binary(<<65,66>>)
iex(4)> IO.iodata_to_binary([65,66,"CD"])
iex(5)> IO.iodata_to_binary([65,66,"CD", <<69, 70, 71>>])

To add to what has already been noted about what IOData is, the main use for IOData is to avoid unnecessary memory allocation and copying.

Let’s say you want to output “Good morning, #{username}” … well, you could create the whole string first and pass that to IO.puts which would then send the bytes on their way:

IO.puts "Good morning, " <> user

But this is going to allocate memory for the whole string, copy the bytes into it from the literal and the contents of the user varaiable … only for IO.puts to then copy that out to some destination (e.g. the console). Seems wasteful!

Would be nice if you could just say, "Here is all my data … " and IO.puts could skip the intermediate allocation and copying. Well, that’s the point of iodata:

IO.puts ["Good morning, ", user]

The output is the same in both cases, but there is not intermediate alloc/copy of memory around! IO.puts just pulls each item from the list of data passed to it and spits it straight out to console one chunk after the other. Much faster, and less memory used.

This is awesome for templating: create an IOList of all the static strings with the variables at the right place, and then when you render the template the only copying needed is to the destination (file, socket, etc.). Keep in mind that binaries can be shared in the BEAM transparently, and suddenly your templates, regardless of where they are called from, will often be sharing the bulk of their memory used for those static strings, even though the end output is customized with the interspersed variables. Phoenix uses this trick, and is one of the reasons it can be so fast.

A lot of Elixir/Erlang functions and APIs accept IOData for this reason …