Hi José,
While the general concept of for-comprehensions is flat_map, the inner most function usually is not. This is because you usually don't want to flatten your values, just the surrounding lists. The previous example will throw an undefined protocol error if the inner map is flat_map because it will try to flatten the tuple {i,j}.
While I am focusing on Enum.product for this first post, it is mostly just a precursor to Stream.product. This isn't exactly a proposition exclusive to Elixir, but a general Stream idea/function that I and a few others have worked on. Building heavily on Matthew Szudzik's "elegant" version of Cantor's pairing function.
The issue with getting the cartesian product (nested iteration) of Streams the default way is that it's unsafe. If the inner Stream doesn't halt, the outer Stream doesn't increment. This means that any number of valid Streams become invalid due to improper enumeration order.
Stream.product(natrual_numbers, natrual_numbers, natrual_numbers) # a, b ,c
|> Stream.filter(fn {a,b,c} -> a*a + b*b == c*c end) # is a right triangle
|> Stream.take_while(fn {a,b,c} -> (a*b)/2 <= max_area end) # has an area less than max_area
Using the nieve approach, like how for works, you end up just enumerating the last enumerable, [{0,0,1}, {0,0,2}, {0,0,3} .... {0,0,9999999} ...]. Despite trying to create a Stream that will produce every combination of 3 natrual_numbers, you end up with the same stream you started with. Just two 0's tacked on the front.
This means normal product functions are unsafe with Streams. If any Stream (other than the outer most loop) doesn't halt, the new Stream has inaccessible values. This is the same issues as concat-ing two Streams. If the 1st doesn't halt, every value in the 2nd is inaccessible.
But there is a way to create the product of N Streams that always safe, and still ordered*. That's the main point of Stream.product. Not only adding safety but allowing you to use Streams is a few new ways.
At this point, I just wanted to focus on the simpler part of this proposal to get the syntax nailed down. But if Enum.product seems redundant, I will move on to the meat of things: Stream.product.