Is this a bug, [H | T]?

In iex, [H|T] is judged as List, I think this is not right. I use Elixir 1.6.

Do you think so?

Thanks.

Nope, not a bug. That’s called an improper list.

Here’s an existing thread about it. What can you do with improper lists?

iex(23)> x = [H|T]
[H | T]
iex(24)> is_list x
true
iex(25)> length x
BUG!!!** (ArgumentError) argument error
    :erlang.length([H | T])

How about this? I’ll read your link and try to understand, thanks for your info.

Lists in erlang/elixir are linked lists, so [1, 2, 3, 4, 5, 6] is syntax sugar for [1 | [2 | [3 | [4 | [5 | [6 | [] ] ] ] ] ] ], where each element contains a value and a pointer to a continuation. As long as the continuation links to another list the list is considered “proper”, while if it points to something else it’s “improper”. Proper lists are what you normally consider a list of values, but e.g. iodata uses improper lists to be able to combine many binary values to a single textual representation without falling into the performance issues of large binary concatination.

1 Like

Quick example of an improper list vs proper list:

# proper list
[ 1 | [ 2 | [] ] ]

# inproper list
[ 1 | 2 ]

The [] at the end of a proper list is used by erlang to determine the end of a list. So functions like length will count the number of elements in a list until it reaches a []. If that final element is missing we call it an improper list.

Incase your wondering what happens if an element in your list is an empty list like: [ [], 1 ]. Remember as @LostKobrakai said above that what we see is shorthand for [ [] | [ 1 | [] ] ] . As such empty lists that are elements won’t cause any trouble

[1] ist equivalent to [1|[]] and both are propper lists.

Thanks for pointing that out, I’ve included extra elements to correct my syntax.

If you look at the types, you’ll see that it’s not a bug in that case either. Here, the type of the parameter for length/1 is list. If you look at some of the basic types supported here, you’ll see maybe_improper_list, nonempty_improper_list, and nonempty_maybe_improper_list, which would be the types you want to use for improper lists. Elixir doesn’t check the types for you, though. You’d need to use dialyzer, but dialyzer should complain if you had this code:

length([H | T])
2 Likes