 
            
            List
A list in elixir is denoted by square brackets [ ], its underlying data structure is a linked list ( single ) as  opposed
to an array. Each element (node) of the list holds reference to the next, this implies that an element of the list is likely
to be stored in a random location in memory.

A list is segemented into two (2) parts head and tail and can contain different datatypes but usually contains
same datatype.
  [head | tail] = [:a, :b, :c, :d, :e] # =>  [:a, :b, :c, :d, :e]
  head #=> :a
  tail #=> [:b, :c, :d, :e]
  [head | tail] = tail # =>  [:b, :c, :d, :e]
  head #=> :b
  tail #=> [:c, :d, :e]
  ["a", 1, :b, 4.5, true, [:a, :b, :c], {:ok, File },  %{name: :kofi, age: 100}]
  "a" # => BitString
   1  # => Integer
  :b  # => Atom
  4.5 # => Float
  [:a, :b, :c] # => List
  {:ok, File } # => Tuple
  %{name: :kofi, age: 100} # => MapList Traversal
Traversing a list in elixir is expensive as it is a linear operation O(n). Considering the list below, it will take
five (5) iterations to reach the end of the list.
  list = [:a, :b, :c, :d, :e]List Update
Given the list below
  names = ["Alice", "Bob"]Prepending ( | ) is fast.
  names = ["John" | names] # => ["John", "Alice", "Bob"]Appending ( ++ ) is slow.
  names = names ++ ["Jane"] # => ["John", "Alice", "Bob", "Jane"]Take Away : Updating a list can be cheap depending on the update approach. it is therefore best practice to
prepend ( | )  instead of append ( ++ ).
Prepending is much faster because you don’t iterate the length of the list to drop an element at the head thus
a constant time O(1).
On the contrary, appending is slower because to drop an element at the tail, you iterate the length of the list
hence a linear time O(n) where n is the length of the list.
Special Kernel Functions For Manipulating List
- hd : Returns the head of List
  hd([1, 2, 3, 4, 5]) # => 1- tl: Returns the tail of list
  tl([1, 2, 3, 4, 5]) # => [2, 3, 4, 5]Elixir list is suited for functional programming