I wonder what a concrete use case for this would be, although I understand the example it feels a bit contrived.
Streams are typically used to avoid fitting the whole collection at once in memory, but if you're going to store it anyway you might be able to use lists in the first place? You might need to reorganize the code and the conditionals a bit though for the initial lazy eval: first? = :rand.uniform() > 0.5 last? = :rand.uniform() > 0.5 maybe_names = if first? or last?, do: get_people() |> without_kids() |> names() |> Enum.to_list() Implementation-wise, due to immutability you can't really have a one-size-fit-all solution, as you mentioned you'd need to pick some backend (or even make it flexible), which makes me think the scope is probably too ambitious and feels more like a candidate for a library than for a general abstraction in core. Or perhaps using something like Cachex is enough in practice if you need to cache some steps and avoid performing some operations twice? (in this case it doesn't feel to be specifically about enumerables) Le mer. 16 oct. 2024 à 08:52, Alex Bruns <alex.h.br...@gmail.com> a écrit : > Elixir streams should have a "pure" version that automatically memoizes > the result (aka lazy lists). E.g. > > s = PureStream.map([1, 2, 3], fn x -> x + 1 end) > Enum.to_list(s) # lazily computes => [2, 3, 4] > Enum.to_list(s) # returns [2, 3, 4] *but* no new computation is done > because we already did the computation and memoized it > > They could allow the caller to provide the memoization implementation to > accommodate process-dict based caching, node-wide caching, cluster-wide > caching, or even system-wide caching via something like a db, and they > could be configured to only cache when computations take a set amount, etc. > > Imagine the following situation: > > @spec get_people() :: Enumerable.t(Person.t()) > @spec without_kids(people :: Enumerable.t(Person.t())) :: > Enumerable.t(Personal.t()) > @spec names(people :: Enumerable.t(Person.t())) :: String.t() > > If I implement these functions to return lists, then > get_people() |> without_kids() |> names() > iterates over everything 3 times. No good if I have a billion people. > > If I implement these functions to return streams then > names = get_people() |> without_kids() |> names() > first_names = Enum.map(&to_first_name/1) > last_names = Enum.map(&to_last_name/1) > iterates over everything twice. > > Okay, but I can do > names = get_people() |> without_kids() |> names() |> Enum.to_list() > first_names = Enum.map(&to_first_name/1) > last_names = Enum.map(&to_last_name/1) > > This works, but it's eager. So, this > names = get_people() |> without_kids() |> names() |> Enum.to_list() > maybe_first_names = if :rand.uniform() > 0.5, do: > Enum.map(&to_first_name/1) > maybe_last_names = if :rand.uniform() > 0.5, do:Enum.map(&to_last_name/1) > will still compute names even though a quarter of the time neither > first_names nor last_names use names. > > Technically, we can already do this today by abusing the fact that we know > how streams work, but of course, nobody wants people doing that. > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to elixir-lang-core+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/68274100-c167-440e-89e3-c039ea5262f0n%40googlegroups.com > <https://groups.google.com/d/msgid/elixir-lang-core/68274100-c167-440e-89e3-c039ea5262f0n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CANnyohaNBqHkPEhODW%2BMjg1jCpN7-C51Vgke809rXxbQ_CcHHA%40mail.gmail.com.