Last week I wrote about Stream patterns in Elixir based on examples I found throughout the Elixir project. I also mentioned that I was having trouble finding good examples of Stream usage. This week I was reading through Chris McCord’s Metaprogramming Elixir . So far it has been a great read and I’ve learned a lot about macros in Elixir. And, more relevant to this post, I found a great Stream example which I would like to analyze and develop into a pattern in this post.

Comprehension over Stream.cycle

Chapter 2 of Metaprogramming Elixir builds a macro to implement a while loop construct. The book builds up the example over several interations so its first version, shown here, is incomplete but illustrative:

# From Chris McCord's “Metaprogramming Elixir” chapter 2 defmacro while ( expression , do : block ) do quote do for _ <- Stream . cycle ([ :ok ]) do if unquote ( expression ) do unquote ( block ) else # break out of loop end end end end

This stream pattern boils down to

for _ <- Stream . cycle ( list ), do : f

The pattern gives us a way to repeat the same function over and over again. The pattern I’ve shown is really equivalent to Stream.repeatedly(f) . The elements of the list, [:ok] in the book, are unused within the do block and the function f . We could ask ourselves, why not just use Stream.repeatedly(f) here in this case? I believe, the example used in the book was choosen to be more illustrative - the block is in the for comprehension rather than an agrument to Stream.repeatedly/1 . This makes the example easier to follow.

But, in addition to clarity, this pattern can lead us to another. Generalizing, we can expand this pattern’s utility.

Comprehension over an infinite stream

We can generalize the pattern to

for x <- an_infinite_stream , do : f

That is, an infinite stream can be used as the source of a for comprehension. This allows us to write an infinite loop, or more accurately an infinite for comprehension. Depdending on the situation this infinite comprehension could be useful.

One example to consider would be a server process which is usually written as an infinite recursion. We can use the pattern to implement something like a loop calling receive over and over again in order to receive messages from other processes. Let’s consider a simple echo server and compare examples of the recursive and comprehension versions:

Recursive version:

defp receive_recursive do receive do arg -> IO . puts ( " #{ arg } " ) end receive_recursive end

Using the comprehension of an infinite stream pattern we could rewrite the server as:

defp receive_stream do for arg <- stream_of_events do IO . puts ( " #{ arg } " ) end end defp stream_of_events do Stream . repeatedly ( fn -> receive do arg -> arg end end ) end

The infinite comprehension version required writing an anonymous function around receive because receive is not a function itself and this makes the code a bit longer. But, the major difference is that there isn’t a really clean way to exit from receive_stream . The recursive solution gives a cleaner path to exiting - by simply not making the recursive call. For example we could rewrite the code like this:

defp receive_loop do receive do :exit -> IO . puts ( " Exiting" ) arg -> IO . puts ( " #{ arg } " ) receive_loop end end

Now, sending the server :exit will cause it to exit. Chapter 2 in Metaprogramming Elixir gives a solution for exiting from a for comprehension. But in some server situations there is never a reason to exit. This comes down to a matter of style: which code do you prefer to read?

If you like this post then you should know that I'm writing a book full of patterns like these called Idiomatic Elixir.



If you inerested in the book then sign up below to follow its progress and be notified when it is launched. You'll also receive two free chapters as a sample of what the book will contain.



Along the way you will also receive Elixir tips based on my research for the book. Please enable JavaScript



Conclusion

This week we looked at another Stream example and generalized it to drive an infinite for comprehension. I worked through an example of a server process and wrote it using both the infinite for comprehension format as well as using the more traditional recursive function. The two forms are essentially equivalent, and I think the real difference is only a matter of style. And, as Chris McCord showed in Metaprogramming Elixir the infinite for comprehension style is more readable and illustrative in some situations.