spencer.christensen

spencer.christensen

Why is [1, 2, 3 | 4] a valid list if lists are linked lists?

I did not formally study Computer Science, so please bear with me if I am missing something obvious. I am studying the inner workings of functional programming concepts and wrote this simplistic map function:

defmodule EnumMap do
  def map(proc, [head | []]) do
    proc.(head)
  end

  def map(proc, [head | tail]) do
    [proc.(head)] ++ map(proc, tail)
  end
end

EnumMap.map(fn x -> x + 1 end, [1, 2, 3, 4])

This produced: [2, 3, 4 | 5].

I thought “that’s weird, why is 5 being explicitly called with a list constructor?”

Aha, then I caught my mistake:

defmodule EnumMap do
  def map(proc, [head | []]) do
    [proc.(head)] # I needed to wrap this result in a list
  end

  def map(proc, [head | tail]) do
    [proc.(head)] ++ map(proc, tail)
  end
end

EnumMap.map(fn x -> x + 1 end, [1, 2, 3, 4])

Producing: [2, 3, 4, 5]

Hooray! However, that then made me wonder!

If Elixir lists are linked lists, why is [2, 3, 4 | 5] allowed? Isn’t that explicitly saying “this is a linked list, except for the final element which is just an integer.”? Is there a benefit to having linked lists where the final element is not in fact a list? Are there certain data structures or algorithms that use these sort of odd linked lists?

Marked As Solved

derek-zhou

derek-zhou

Because you have a subtle bug in your code, as you find out later.

[2, 3, 4 | 5] is an improper list. You should avoid it, but there is nothing in the language to forbid it.

If you are interested in improper lists, you can read: Making sense of Elixir (improper) lists

Also Liked

D4no0

D4no0

You can also refactor that recursion to use tail call optimization.

sabiwara

sabiwara

Elixir Core Team

The cons operator is indeed much more idiomatic in this case, but this is mostly a style preference, there is no performance or behavior implication.
Both will generate the same bytecode when prepending a known number of elements with ++.

spencer.christensen

spencer.christensen

Thank you for sharing that article. Super interesting read! Also made me realize my use of lists could be optimized:

My original implementation used appending to create the list:

[proc.(head)] ++ map(proc, tail)

However I could instead use the cons operator to actually form a linked list as it is intended:

[proc.(head) | map(proc, tail)]

It also was redundant in its application of [proc.(head)] and could be simplified for just matching against an empty array.

Better version:

defmodule EnumMap do
  def map(_proc, []), do: []
  def map(proc, [head | tail]) do
    [proc.(head) | map(proc, tail)]
  end
end

EnumMap.map(fn x -> x + 1 end, [1, 2, 3, 4])
sabiwara

sabiwara

Elixir Core Team

It is a very good suggestion as an exercise, but for this particular case which is building a list it might not be the best approach, actually :lists.map/2 (used under the hood by Enum.map/2) is implemented with body recursion:

Also see: Erlang -- The Seven Myths of Erlang Performance.

spencer.christensen

spencer.christensen

As somebody new to functional programming, can I ask whether this means simply using an accumulator? Or is there some more idiomatic-Elixir method for this? E.g.

defmodule EnumMapTail do
  def map(proc, list) do
    run_map(proc, list, [])
  end
  defp run_map(_proc, [], acc), do: Enum.reverse(acc)
  defp run_map(proc, [head | tail], acc) do
    run_map(proc, tail, [proc.(head) | acc])
  end
end

Where Next?

Popular in Questions Top

openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

We're in Beta

About us Mission Statement