A higher-order function is a function that does at least one of the following:
All of these use higher-order functions as the primary construct.
If fact, functions as arguments are so ubiquitous that there is a special syntax for it.
A block is a higher-order function which is an argument to a method call
A Rails plugin by yours truly:
You can read more on Ruby blocks and how they are special in a post by Yehuda Katz The Building Blocks of Ruby
when you can build language constructs with blocks and
each Ruby can implement the same set of higher-order methods on enumerables as functional languages:
filterNotin other languages)
filterin other languages)
All these are found in module Enumerable which can be included in any collection class which implements
Each such method creates a new collection. They can be huge…
We can rewrite this code to process each word one by one. That’s one solution…
This is what we have, a sequential execution:
Our blocks working concurrently, and processing elements one by one:
This is what we have
This is what we need
The second modus operandi is on demand, it is lazy.
Fibers were added in Ruby 1.9
Here’s what the documentation says:
Fibers are primitives for implementing light weight cooperative concurrency in Ruby. Basically they are a means of creating code blocks that can be paused and resumed, much like threads. The main difference is that they are never preempted and that the scheduling must be done by the programmer and not the VM.
The goal of this implementation is only to demonstrate how laziness can be achieved with the help of fibers.
This is neither a complete nor a true implementation of
Enumerable::Lazy in Ruby 2.0.
Beauty and completeness have been sacrificed in favor of simplicity.
Enumerable::Lazy in Ruby 2.0 uses
Enumerator added in Ruby 1.9 as the basis.
Enumerator uses fibers. This proof of concept code uses fibers directly because
Enumerator is beyond the scope of this presentation.
Let’s create a wrapper class which will contain a fiber (which in its turn will contain the block defined in the client code)
Let’s add a method in
Enumerable which will create an instance of this wrapper out of an enumerable:
The wrapper object should have an API similar to
each should be non-lazy,
select and others should be lazy.
Let’s implement the non-lazy behavior and
each. We just access the underlying enumerable via a fiber, no added logic here:
The value of the block is also returned from the fiber, so we have to rewrite
each to ignore the last value returned from the fiber.
select. We’ll convert the client code block to an explicit function object for the sake of readability.
We are incapsulating the client code block in yet another function which implements the logic of
select, but it calls
Fiber.yield to return the result, because this function will be called from within a fiber.
select return a new
LazyPanda instance incapsulating new logic. In a way, we are dealing with a Russian doll pattern: the first
LazyPanda incapsulates a real Enumerable, the next
LazyPanda incapsulates the first
LazyPanda, and so on.
Now we need to modify the constructor. The constructor should have a second optional function parameter. In case such a function is present, the constructor places this function into a fiber instead of the default
No output. It is lazy. This code has only created the Russian doll structure. No block has been executed.