The answers provided so far, although correct do not really address the core of your problem. The core of your problem here is that you are unfamiliar with how Enum.each, Enum.map, for x <- a etc. are implemented under the hood.
Enum.map under the hood is nothing but a recursive function that accepts two inputs:
- a list
- a function that needs to be applied to each element in that list
The return value of this recursive function is a new list.
To clarify even more:
say you have as input the list [1, 2, 3, 4] and a function: fn e -> e * 2 end then you will get the new list [2, 4, 6, 8]
Enum.map could simply be implemented like this:
defmodule MyEnum do
def map(list, fun) do
if Enum.empty?(list) do
[] # Null case reached, stop recursing.
else
[head | tail] = list # obtain the first element in the list, assign that value to `head`. Store the remaining list inside the variable `tail`
projected_value = fun.(head) # This is where your function gets applied
[ projected_value | map(tail, fun)] # Recurse
end
end
end
So MyEnum.map([1,2,3], fn e -> e * 2 end) will return to you the list: [2, 4, 6]
Now suppose that you utilize the function IO.puts/2 here, like:
MyEnum.map([1,2,3], fn e -> IO.puts(e) end), the variable projected_value will eventually be the return value of IO.puts/2. It prints the value to the screen, and it returns an :ok atom.
This means that the list you are building up in this case would be accumulating the atoms :ok over each new recursion.
The comprehension for x <- a under the hood is something similar to Enum.map, a recursive function that will apply a provided function to each element of your list, but just with a different syntax.
Enum.each is also a recursive function under the hood, but it will just loop over every element, not caring about accumulating anything in the process. This is how you could implement it:
defmodule MyEnum do
def each(list, fun) do
if Enum.empty?(list) do
:ok # Return `:ok` to communicate the entire list has been traversed
else
[head | tail] = list
fun.(head) # Only apply the function to the element of the list, and do not care about its return value
each(tail, fun) # Continue to traverse the list recursively
end
end
end
As you can see, this function will always return :ok and Enum.map will always return a new list.
All of this is elementary stuff, so:
- If your understand what is happening under the hood and;
- you understand that in essence
Enum.map and for x <- [1,2,3] are basically one and the same recursive function with just a different interface
|> then all your questions should be clarified.
In addition, the same constructs are provided in Javascript as well. If you do [1,2,3,4].map or [1,2,3,4].each in JS, they do the exact same thing as in Elixir, so you could find more clarity in understanding these constructs in Javascript first if you still have a problem understanding the differences.