I think that fold
s are best understood when source is provided.
Therefore I’ll do a quick translate of my haskell-lecture-notes to elixir
without taking optimisations into accont. I use my haskell notes since they are
(nearly) always around my desk 
defmodule Folds do
def foldl(list, fun, acc)
def foldl([], _fun, acc), do: acc
def foldl([x|xs], fun, acc), do: foldl(xs, fun, fun.(x, acc))
def foldr(list, fun, acc)
def foldr([], _fun, acc), do: acc
def foldr([x|xs], fun, acc), do: fun.(x, foldr(xs, fun, acc))
end
Using these definitions, I will use each of the folds with 2 very simple
functions:
foldr [1,2,3], &(&1 + &2), 0
foldl [1,2,3], &(&1 + &2), 0
foldr [1,2,3], &([&1|&2]), []
foldl [1,2,3], &([&1|&2]), []
1. foldr [1,2,3], &(&1 + &2), 0
=> foldr [1|[2,3]], &(&1 + &2), 0
=> &(&1 + &2).(1, foldr([2,3], &(&1 + &2), 0))
=> &(&1 + &2).(1, foldr([2|[3]], &(&1 + &2), 0))
=> &(&1 + &2).(1, &(&1 + &2).(2, foldr([3], &(&1 + &2), 0)))
=> &(&1 + &2).(1, &(&1 + &2).(2, foldr([3|[]], &(&1 + &2), 0)))
=> &(&1 + &2).(1, &(&1 + &2).(2, &(&1 + &2).(3, foldr([], &(&1 + &2), 0))))
=> &(&1 + &2).(1, &(&1 + &2).(2, &(&1 + &2).(3, 0)))
=> &(&1 + &2).(1, &(&1 + &2).(2, 3 + 0))
=> &(&1 + &2).(1, &(&1 + &2).(2, 3))
=> &(&1 + &2).(1, 2 + 3)
=> &(&1 + &2).(1, 5)
=> 1 + 5
=> 6
2. foldl [1,2,3], &(&1 + &2), 0
=> foldl [1|[2,3]], &(&1 + &2), 0
=> foldl [2,3], &(&1 + &2), &(&1 + &2).(1, 0)
=> foldl [2,3], &(&1 + &2), 1 + 0
=> foldl [2|[3]], &(&1 + &2), 1
=> foldl [3], &(&1 + &2), &(&1 + &2).(2, 1)
=> foldl [3], &(&1 + &2), 2 + 1
=> foldl [3|[]], &(&1 + &2), 3
=> foldl [], &(&1 + &2), &(&1 + &2).(3, 3)
=> foldl [], &(&1 + &2), 3 + 3
=> foldl [], &(&1 + &2), 6
=> 6
3. foldr [1,2,3], &([&1|&2]), []
=> foldr [1|[2,3]], &([&1|&2]), []
=> &([&1|&2]).(1, foldr([2,3], &([&1|&2]), []))
=> &([&1|&2]).(1, foldr([2|[3]], &([&1|&2]), []))
=> &([&1|&2]).(1, &([&1|&2]).(2, foldr([3], &([&1|&2]), [])))
=> &([&1|&2]).(1, &([&1|&2]).(2, foldr([3|[]], &([&1|&2]), [])))
=> &([&1|&2]).(1, &([&1|&2]).(2, &([&1|&2]).(3, foldr([], &([&1|&2]), []))))
=> &([&1|&2]).(1, &([&1|&2]).(2, &([&1|&2]).(3, [])))
=> &([&1|&2]).(1, &([&1|&2]).(2, [3]))
=> &([&1|&2]).(1, [2,3])
=> [1,2,3]]
4. foldl [1,2,3], &([&1|&2]), []
=> foldl [1|[2,3]], &([&1|&2]), []
=> foldl [2,3], &([&1|&2]), &([&1|&2]).(1, [])
=> foldl [2,3], &([&1|&2]), [1]
=> foldl [2|[3]], &([&1|&2]), [1]
=> foldl [3], &([&1|&2]), &([&1|&2]).(2, [1])
=> foldl [3], &([&1|&2]), [2,1]
=> foldl [3|[]], &([&1|&2]), [2,1]
=> foldl [], &([&1|&2]), &([&1|&2]).(3, [2,1])
=> foldl [], &([&1|&2]), [3,2,1]
=> [3,2,1]
Personally I think, the best way to understand higher order functions, is just
to take a small example output, look at the definition and go through it by
hand. We had to do this a lot during FP exercises…
Pen and Paper, vim, emacs, whatever you like most, but beeing capable of
monospaced fonts helps a lot 
edit
I’ve seen @alamba78’s latest reply to late, but I had not used that function anyway, since it produces improper lists!
Improper lists are something you want to avoid, that are lists, where the last “element” is not the empty list, but something else. They can really confuse your code. The example you are showing is even nesting them…