0

In Elixir, why does the comprehension for x <- 3..4, do: x * 2 result in [6, 8] but for x <- 3..4, do: x * 3 results in '\t\f' ?

I'm executing this in the iex repl.

ybakos
  • 8,152
  • 7
  • 46
  • 74
  • Take a look at [this answer and question](https://stackoverflow.com/questions/63473722/elixir-comprehension-returning-a-star-character/63476012#63476012) – sbacarob Dec 03 '20 at 23:50

1 Answers1

3

This is one of the most confusing gotchas in Elixir: the "humanizing" of charlists. See this post for a question revolving around this same issue.

Charlists are lists of integer codepoints, used to represent un-encoded strings (more common in older Erlang libraries from the days before encodings were baked in). So if you have a list of integers, Elixir (for historical reasons), has to guess whether or not you meant to represent a 'string' -- so if all the integers in that list are below 127, Elixir assumes that this must be a charlist that represents 'string' data and it prints out the corresponding ASCII characters to make a readable string.

You can reveal the actual inner representation by setting the charlists: :as_lists option in your calls to IO.inspect/2:

iex(1)> result = for x <- 3..4, do: x * 3
'\t\f'
iex(2)> IO.inspect(result, charlists: :as_lists)
[9, 12]
'\t\f'

or more simply:

iex> IO.inspect([9, 12], charlists: :as_lists)
[9, 12]
'\t\f'

This shows us the expected integers (9, 12), and if we bother to look these up in a Unicode or ASCII code chart, we would see that the \t and \f characters are represented by codepoints 9 and 12 respectively.

Keep in mind that these representations of the data are just visual -- the underlying data is 100% the same:

iex> [9, 12] === '\t\f'
true
Everett
  • 8,746
  • 5
  • 35
  • 49