36
votes

New to Elixir from Ruby and I'm trying to print out a map's array values using the following code:

map-script.ex

list = [0, 1]
map = %{0 => [1, 2], 1 => [2, 3]}

Enum.each list, fn n ->
  IO.puts map[n]
end

Output:

^A^B
^B^C

What am I doing wrong? Elixir looks similar to Ruby but it behaves differently....

1
It's a big mistake to assume that because Elixir looks like Ruby it will behave like Ruby as well.Onorio Catenacci

1 Answers

74
votes

You should use IO.inspect instead of IO.puts to print the internal representation of a value:

iex> IO.puts [1, 2]
^A^B                          # prints "^A^B"
:ok                           # return value of IO.puts

iex> IO.inspect [1, 2]
[1, 2]                        # prints "[1, 2]"
[1, 2]                        # return value of IO.inspect

However you might still get problems with some special values:

iex> IO.inspect [97, 98]
'ab'                          # prints "'ab'"
'ab'                          # return value of IO.inspect

To explain this behavior we need to understand how strings work in Elixir. There are two kinds of strings. Binary strings (double quoted) and character lists (single quoted). Internally, these are built from simpler primitives. Binary strings are built from binaries, and character lists are built from lists:

iex> "ab"                    # a binary string
"ab"

iex> <<97, 98>>              # the same string in binary syntax
"ab"

iex> 'ab'                    # a character list
'ab'

iex> [97, 98]                # the same character list in list syntax
'ab'

It might seem confusing at first, but this happens due to two things. First, there is no built in string type, as we have seen strings are built from other primitive types. Second, there are no user defined types in Elixir. So when Elixir sees a list with only integers, it tries to print it as a string for convenience. However, under the hood it is still just a list of integers.

In the examples above, 97 and 98 represent the unicode code points for the characters a and b so these will be shown as a string, even if using IO.inspect.

Now you can see why it printed ^A^B in your examples – these are just control characters from the ASCII encoding, which happen to be represented by the codepoints 1 and 2.

However, you can pass the option char_lists: :as_lists in order to print raw lists without an attempt to do such a conversion:

iex> IO.inspect [97, 98], char_lists: :as_lists
[97, 98]                                         # prints '[97, 98]'
'ab'                                             # Iex still shows the return value as 'ab'

If you open up iex and type h Inspect.Opts, you will see that Elixir does this kind of thing with other values as well, specifically structs and binaries.