Keyword.get_values() performing ascii translation on values for certain numbers

The below code is returning “Here’s my list: efl” for me, substituting the ASCII numeric equivalents for 101, 102, and 108. Does anyone feel that this is a bug that should be reported? If I add a higher number like 4000 to the :top key, then Keyword.get_values() works correctly. Looks like a bug to me. I’m running Elixir 1.10.3.

defmodule Trythis do
    def findit(jay) do
        hh = Keyword.get_values(jay, :top)
        IO.puts("Here's my list: #{IO.inspect(hh)}")
    end
end

kl = [top: 101, height: 201, bottom: 22, top: 102, bottom: 40, top: 108]
Trythis.findit(kl)

You still have the numbers in place, it’s just that IO.puts and IO.inspect see those integers are printable ASCII characters so they choose to print them as characters.

If you want it not to do that, try this code:

IO.inspect(hh, charlists: :as_lists, label: "Here's my list")

The original behaviour is not a bug, simply a default convenience which you can opt out from.

5 Likes

It should return :ok, as thats the only value that IO.puts/2 is able to return.

It my print Here's my list: efl though. But also it should print 'efl', as a sideffect of calling IO.inspect.

If you want to display a list of integers always as a list of integers, you can use List.to_string/1.

Yes, my use of IO.inspect was incorrect. But you do see that Keyword.get_values() is returning chars unless I replace 108 with 400 and then its all numbers as we’d expect. Isn’t this inconsistent?

iex(1)> kl = [top: 101, height: 201, bottom: 22, top: 102, bottom: 40, top: 108]
[top: 101, height: 201, bottom: 22, top: 102, bottom: 40, top: 108]
iex(2)> Keyword.get_values(kl, :top)
'efl'

No, it is how inspection works.

Any list that only consist of numbers in “printable” ASCII range (10 to 127 iirc) are inspected as charlist by default.

You still can treat it as list of numbers anywhere, as it is a list of numbers.

It’s just representation, it’s not a change in value or type.

Ps: https://hauleth.dev/post/treachery-of-representation/ not directly about charlists, but in general the same problem

3 Likes
'efl' == [101, 102, 108]
#⇒ true
2 Likes

Yes, its just representation but why does Keyword.get_values(kl,:bottom) return a list of numbers? Its just inconsistent – and does Keyword.get() ever return chars? This is not the robustness I expect of a mature language. I’m aware that the Elixir type system is different and that this behavior isn’t inconsistent in that light.

Both return a list of numbers, which in one case fullfils a given predicate and in one case not.

The predicate is Enum.all(list_to_inspect, &List.ascii_printable?/1), if this returns true then the list is inspected as a charlist, if it returns false it is represented as a list of integers.

https://hexdocs.pm/elixir/List.html#ascii_printable?/2

PS: Keyword.get/3 never returns a “char”, it returns whatever the value is.

4 Likes

To be clear, I want numbers to appear. Unfortunately, even with charlists: :as_lists I still get ‘efl’ from the IO.inspect(). Are you not getting the same? Is my only option to include a 0 in the list? Why does print formatting have to be so hard – haven’t we already figured this one out (in other languages)?

In what exact case?

For me it works…

iex(1)> IO.inspect ~c'efl', charlists: :as_lists
[101, 102, 108]
'efl'

Where the first line is the actual printed value by IO.inspect and the second line is the inspection of its returned value.

Though if you want to present a list of numbers to a user, you shouldn’t rely on IO.inspect or the Inspect protocol at any time. Its meant for debugging and it might chance how it represents individual values without notice!

If you need reliable output use one of the various to_string functions from the various modules as you need them.

1 Like

I’m trying to get a numeric list to appear using string interpolation (as in the code of my original post). So, even using the option of charlists: :as_lists in IO.inspect will not yield the numeric list I so humbly wish for. While IO.inspect returns the numeric list, it’s printing ‘efl’.

You need inspect not IO.inspect.

Thanks you are indeed correct! The code below will print this output to the screen:
User report: Trey is 19 years of age with [101, 102, 108]

defmodule User do
  defstruct name: nil, age: 19, id: 100, active: true

end

defmodule Tryit do
    def doit do
      pp = [top: 101, height: 201, bottom: 22, top: 102, bottom: 40, top: 108]
      kk = Keyword.get_values(pp, :top) |> inspect(charlists: :as_lists)
      jj = %User{name: "Trey"}
      IO.puts("User report: #{jj.name} is #{jj.age} years of age \
with #{kk}")
    end
end

Tryit.doit()