ChrisCoy

ChrisCoy

Questions about Streams

Hi everyone, hope y’all doing well, I am quite new to elixir and functional programming in general and I never really used Streams in another laguange before, so I have some questions about Streams.

file = File.stream!("elixir_is_funny.txt")
file |> Enum.map(&foo/1) |> Enum.map(&foo/2) |> Enum.map(&/foo3) |> Enum.take(1)

  • each Enum.map will it execute just one time? If yes, how? how does it knows that i will take only one?

  • I’ve read that in order to Enum works, you have to implement a iterable in the module, how that works with streams?

  • I’ve made some tests, and in some cases it stops being a stream, how can i work with streams without transforming it?

  • I’ve heard about the term lazy load, what does that mean? is Streams lazy loaded?

Well thats it, sorry for my english, it is not my first language :smiley:

Most Liked

LostKobrakai

LostKobrakai

Before you can truely understand streams in elixir you need to understand enumerables.

Enumerable is a protocol for collections of data, which can be iterated over using reduce at its core. An enumerable can be e.g. a list, which already exists completely in memory ready to be iterated over. It can however also be “lazy” in that the actual values the collection exists of are only computed or pulled into memory once the enumerable is iterated / reduced over.

A simple case for a lazy enumerable in elixir is Range. It models a sequence of interger values, but the struct only stores the lower and upper boundary of the sequence. 2..15 only stores a struct containing 2 and 15. Only once you start iterating over the range the values between those two numbers are computed – so 3, 4, 5, ….

So that’s Enumerable. Now there’s also Enum and Stream.

Enum provides APIs to work with Enumerable data. It is eager in the sense that each call to an Enum function will iterate over as much of the enumerable collection as the operation would require. E.g. Enum.map will iterate over the complete input and return a list of mapped values for each item in the input collection. Enum.take(input, 1) will take out the first item from the input and be done.

The return value of Enum apis might be another Enumerable, which is why you can often see chained calls to its apis, but it’ll never be another lazy enumerable.

Now the difference with Stream is that its apis will always return lazy enumerables. So the input is wrapped with some lazily computable operation, which gives you a lazy enumerable. Some functions don’t even require an enumerable as input, but build an lazy enumerable from other input, others wrap existing enumerables – them being lazy or not.

Therefore the answers to your questions:

Yes, each Enum.map will execute once, but not just for a single element of your initial input. They’ll all map over all lines of your file and only the last step will discard all the work, besides for the first item in there. Previous steps have no idea you’ll discard stuff in the end.

See above. The protocol is Enumerable and the way it works does allow for both lazy and “not so lazy” enumerables.

There’s no “stream” datatype and also things on the beam are immutable. So there’s no way to change something without changing it. But I hope I explained enough before that streams don’t just stop being streams. If you want to retain an lazy enumerables lazyness you add additional operations using Stream apis. Once you start using Enum operations become eager.

Lazy really only refers to the fact that items of an enumerable collection somehow become available “on demand”. This could be being loaded on demand or computed, …

Aetherus

Aetherus

Let’s do an experiment.

  1. Create a text file with the name nums.txt and the following content:
    1
    2
    3
    
  2. Open iex
  3. Try File.stream!("nums.txt") |> Enum.map(&IO.inspect/1) |> Enum.map(&IO.inspect/1) |> Enum.take(1), what do you see?
  4. Change Enum.map to Stream.map, what do you see?
  5. Change Enum.take(1) to Enum.take(2), what do you see?
Aetherus

Aetherus

This is what I always think of Stream and Enum.

something_enumerable  # <-- upmost stream
|> Stream.do_something(...)
|> Enum.do_something(...)
|> Enum.do_something(...)  # <-- downmost stream

Enum

When you call Enum.do_something, it always tells its upstream “give me everything you have, NOW!”, then it iterates through all the things it gets and does some work to them, then returns something solid (like a list or a map).

Stream

When you call Stream.do_something, it tells the upstream “I’ll ask you for a value whenever I’m asked for a value. Other times I just sit there doing nothing.” So, if there’s no need for a value, it does nothing. The return value of Stream.do_something is not that solid.

Common functions

If some job can be done in a per-value way, like transforming a value to another value based on that value only, then such functions can be found in both Stream and Enum, like map and filter.

Enum-only functions

If something can only be done considering the whole Enumerable, then such functions can only be found in Enum, like sort and group_by and reduce.

Stream-only functions

Because Stream is lazy, it has the potential of generating an infinite number of values. For example, Stream.repeatedly(&:rand.uniform/0) can generate an infinite number of random float numbers, not at once, but whenever you need a float. Such “value generating functions” are only found in Stream.

Where Next?

Popular in Questions Top

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

We're in Beta

About us Mission Statement