Hello, world everyone
Context
I started to learn Elixir recently and I have a lot of simple questions 😅
I’m reading the Elixir in Action
and I finished the first three chapters 🎉
I really enjoyed how Saša explained the basic concepts, for instance, introducing lists and explaining why we should push an element to the top of the list (with illustrations). He also described the big O notation for several basic operations and other aspects related to the compiler and Erlang VM memory allocation.
However, the part of Streams was very compact (compared to the part about lists). I know that it is a lazy enumerable and I understood (I hope) how to use it. But I missed some details about how Elixir compiler handles it.
He said that There are no special tricks in the Elixir compiler
, describing that the secret is the anonymous functions
and when the computation needs to be materialized, the consumer code can call the lambda.
. And this last phrase really made to think about it all day long: Even though you stack multiple transformations, everything is performed in a single pass when you call Enum.each
.
Problem
How does Elixir convert a stream pipeline to just one iteration? Does it simply get the functions that should be applied and serializes it? Assuming it, do these two pipelines are equivalent? 👀
stream = 1..3
|> Stream.map(&IO.inspect(&1))
|> Stream.map(&(&1 * 2))
|> Stream.map(&IO.inspect(&1))
Enum.to_list(stream)
Enum.each(1..3, fn x ->
IO.inspect(x)
|> Kernel.*(2)
|> IO.inspect
end)
Bonus question
Considering this new version of previous code:
Enum.each(1..3, fn x ->
IO.inspect(x)
|> Kernel.* 2
|> IO.inspect
end)
Why does the example above not return an error or behave like the first one?
Isn’t the parentheses optional in the |> Kernel.*
? :thinking_face:
If I had to take a guess, I'd say it happens because |> Kernel.*
can be lazy evaluated (for some reason that I don’t know) and only the number 2
is provided to next step (|> IO.inspect
). Or I maybe I found a bug or I should read these chapters again 😆