Member-only story
How Lazy Evaluation Works in Ruby
Let’s be lazy — iterate only what we really need!
During our day-to-day work, we as developers quite often have to deal with big data or — sometimes — data of unknown size. We parse big files and import the stuff we need, we are scraping some data from various websites in search of something, or we are just working with some big data from our own database to transform it and do something with it.
To do so efficiently we have many options to fall back to. Ruby, like most languages, gives us a lot of options to handle such situations. When working with files we can read them line by line, we can divide the data into smaller chunks with the help of self-written methods and passing around blocks. But, we have another option as well, that isn’t so often used: lazy enumeration.
What is Lazy Enumeration?
Lazy enumeration means that an expression is only processed when we are working with the result. There are languages that are lazy by default, like Haskell, and many others are implementing a way to lazily evaluate expressions.
With lazy enumeration, it is possible to create a pipeline of transformations that are processed only when needed, and if needed the pipeline is processed as a whole for one item at a time. A small code example will make this clear:
When you run this snippet you will notice that the puts inside the map
and the take_while
block are called one after the other. This means that the item is flowing from one block to the next before proceeding to the next item in the array. Contrary, if you remove the call to the lazy
method the blocks are printing all items to the terminal before proceeding to the next step in the pipeline, so the steps are processed sequentially for all items in the array.
Let’s see this ourselves:
Enumerator::Lazy
item: one
item: eno
item: two
item: owt
item: three
item: eerht
[["eno", "four"], ["owt", "five"], ["eerht", "six"]]