Is this a bug with numbers in a List?

Has anyone encounter this before? I reinstalled Erlang 25.01, Elixir 1.13.4
Something happens between 7 and 12 with the list?

iex()> [5,6]
[5, 6]
iex()> [6,7]
[6, 7]
iex()> [7,8]
'\a\b'
iex()> [6,7]
[6, 7]
iex()> [9,10]
'\t\n'
iex()> [11,12]
'\v\f'
iex()> [13,14]
[13, 14]
iiex()> [8,10]
'\b\n'
iex()> [5,10]
[5, 10]
iex()> [4,5] |> Enum.map(& &1 * 3)
[12, 15]
iex> [4,5] |> Enum.map(& &1 * 2)
'\b\n'
iex> {a,b} = {7,8}
{7, 8}
iex> 7+8
15
iex> 7+2
9
iex> [3+1,4+2]
[4, 6]
iex> [6+1,7+1]
'\a\b'
iex(49)> l = [7,8]
'\a\b'
iex(50)> l
'\a\b'
iex(51)> List.first(l)
7
1 Like

I can’t remember as far back as Elixir 1.7 but what you are seeing is perfectly normal. In Elixir and erlang, a list of numbers that in the ASCII range is indistinguishable from a charlist.

When iex evaluates an expression, it inspects the the result which outputs to your terminal. By default, if iex sees that a list is also a charlist it will output it in that format. So you see \a\b rather than [7, 8].

Rest assured the underlying data is always [7, 8]. Its only the output display that is changing.

You can experiment by doing:

iex> inspect [7,8]
"'\\a\\b'"

iex> inspect [7,8], charlists: false
"[7, 8]"

iex> [7,8] == '\a\b'
true
2 Likes

Yes, ok, I saw you can still make calculations, but why this range, and why is the tuple rendering correctly? At least, you have the same result on your machine, do you? Good to know because I thought my computer was dying… This doesn’t really make sense, does it?

They are all rendering correctly, there is no bug. A tuple is not a list and therefore unrelated to the charlist behaviour you are seeing. The background is described in the link I put in the previous post and you can read more about it there.

3 Likes

Yes, everyone has been bitten once by this…

But it’s because in Erlang, a list of characters is just a list of numbers, and if it is possible to display as chars, it will.

iex(1)> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]   
'hello world'

# Adding a zero will disallow this...
iex(2)> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0]
[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0]

# You can use it as a list...
iex(3)> 'hello world' ++ [0]                 
[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0]

# or like this
iex(4)> [0 | 'hello world']                                      
[0, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

# even like this, if You are a fan of 2001 space odyssey's hidden message...
iex(5)> Enum.map 'ibm', & &1 - 1
'hal'
3 Likes

Just to confirm my understanding of this.
Is it right, that even the compiler can not distinguish both:

iex(1)> quote do: 'abc'
'abc'
iex(2)> quote do: [97,98,99]
'abc'

So sth like an charlists: :auto option for inspect is impossible.

iex(3)> IO.inspect('abc', charlists: :auto)               
'abc'
'abc'
iex(4)> IO.inspect([97,98,99], charlists: :auto)
[97, 98, 99]
'abc'
1 Like

Yes, because there is nothing to distinguish: It’s a list of integers.

The only thing different is how such lists of integers are interpreted for human consumption:
Either print single quotes with characters based on the ascii values of the integers or print square brackets and the decimal values for the integers separated by commas. Which one is the the more expected one can only be guessed by heuristic (are all integers in the ascii range?), which is bound to be wrong at times, especially when not interacting with erlang code.

6 Likes

For my recording:

iex(1)> for i <- 2..6, do: i
[2, 3, 4, 5, 6]
iex(2)> for i <- 7..13, do: i
'\a\b\t\n\v\f\r'
iex(3)> for i <- 5..14, do: <<i :: utf8>>
[ <<5>>, <<6>>, "\a", "\b", "\t", "\n", "\v", "\f", "\r", <<14>> ]
for h <- 0..15 do
  for l <- 0..7 do
    [h * 8 + l] |> inspect |> IO.write
    IO.write(" ")
  end
  IO.write("\n")
end
[0] [1] [2] [3] [4] [5] [6] '\a' 
'\b' '\t' '\n' '\v' '\f' '\r' [14] [15] 
[16] [17] [18] [19] [20] [21] [22] [23] 
[24] [25] [26] '\e' [28] [29] [30] [31] 
' ' '!' '"' '#' '$' '%' '&' '\'' 
'(' ')' '*' '+' ',' '-' '.' '/' 
'0' '1' '2' '3' '4' '5' '6' '7' 
'8' '9' ':' ';' '<' '=' '>' '?' 
'@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 
'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 
'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 
'X' 'Y' 'Z' '[' '\\' ']' '^' '_' 
'`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 
'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 
'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 
'x' 'y' 'z' '{' '|' '}' '~' [127]