I think that this is definitely done better as a library than adding it to the core, because fundamentally memoization is about keeping state (the prior state values of the stream), which would *probably* require the use of Agent by default. The precise mechanism used for keeping state will differ based on the application needs and environment (if working in a memory constrained environment like would be typical for a Nerves project, one should not be using memory caching). There are two related things which Elixir delegates to third-party libraries, providing only a basic (though useful) implementation: timezone database and calendar support. I don't think that Elixir could meaningfully provide a baseline caching mechanism that would satisfy the needs of users.
-a On Wed, Oct 16, 2024 at 5:58 PM Alex Bruns <alex.h.br...@gmail.com> wrote: > It's true that you could always change existing code to support bespoke > loading logic. However, this introduces an undesirable form of incidental > coupling. There is no fundamental reason that the callsite at which names > (or maybe_names) needs to know about how it will be used, and doing so has > a real cost. In your example, you've hoisted the conditional logic to the > top level, but what if that is not possible because it depends on some > local context? Such a hoisting operation could introduce a massive amount > of complexity. My opinion is basically that we have the ability to provide > better performance characteristics for a large number of programs without > introducing additional complexity and we should do that. > > As for this being a library, I think that's a really good point. There's > nothing stopping me, or someone else, from doing this in user space. That > said, one could say the same thing about streams. I think the primary > reason I support adding this to core would be to push widespread adoption > as, without core approval, the only devs who would use this would be those > that already recognize its value, limiting adoption. > > Wrt caching individual steps, this is suboptimal in situations where you > have a lot of steps. Still, probably a great 90% solution for lots of use > cases. > > On Wed, Oct 16, 2024 at 5:20 AM Jean Klingler <sabiw...@gmail.com> wrote: > >> 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 a topic in the >> Google Groups "elixir-lang-core" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/elixir-lang-core/GHUFTnJL6hQ/unsubscribe >> . >> To unsubscribe from this group and all its topics, 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 >> <https://groups.google.com/d/msgid/elixir-lang-core/CANnyohaNBqHkPEhODW%2BMjg1jCpN7-C51Vgke809rXxbQ_CcHHA%40mail.gmail.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/CAOysmgGPwB83%3DaThnqTACUnD5j9UwwAr_A3D%3DUrZOx-eM1b%2B_A%40mail.gmail.com > <https://groups.google.com/d/msgid/elixir-lang-core/CAOysmgGPwB83%3DaThnqTACUnD5j9UwwAr_A3D%3DUrZOx-eM1b%2B_A%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > -- Austin Ziegler • halosta...@gmail.com • aus...@halostatue.ca http://www.halostatue.ca/ • http://twitter.com/halostatue -- 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/CAJ4ekQs41_ApOweoCv9MHSvfNGFGA5oiNUDBrHS3%2BErMC-dw4w%40mail.gmail.com.