Dan Newcome on technology

I'm bringing cyber back

Clojure lazy sequences, ISeq

with one comment

Functional languages like Clojure support lazy evaluation of expressions. This contrasts sharply with languages like C# where every expression is evaluated immediately. In order to get something that resembles a lazy sequence in Clojure would be to use IEnumerable and yield.

Ok so I should probably dig into comparing Clojure sequences with C# Iterators, but I’m going to do that later on. First I want to explore Clojure sequences since they are little more slippery than I first imagined.

The Clojure docs would initially have you believe that every data structure provided by the core library is already a sequence, implementing ISeq. This isn’t explicitly true. Let’s take a look at a few examples:

Is a vector a seq?

user=> (seq? [1 2])
false

Nope. How about a map?

user=> (seq? {:foo 1})
false

Again, no. What is the deal? Aren’t all of these things supposed to support ISeq? Ok, what about a list?

user=> (seq? (list 2 3))
true

Ok, now we’re getting somewhere. So a list implements ISeq by default. So, since a vector isn’t a seq, we shouldn’t be able to take the first item using ‘first’ right? Let’s try it:

user=> (first [1 2])
1

Ooops. What’s going on? Reading the docs more closely reveals that the above is actually calling a function called ‘seq’ on its argument before evaluation. So the following expression is actually equivalent:

(first (seq [1 2]))
1

Now, what does it look like when we create a sequence from a vector?

user=> (seq [2 3])
(2 3)

It looks like a list. Let’s see if it is.

user=> (= (seq [2 3]) (list 2 3))
true

Wow. Let’s double-check that result.

user=> (= (seq [2 3]) [2 3])
true

Ok, that’s strange. We know that a vector is not a seq by default, so there must be some coercion going on here.

So now how about using lazy sequences? Let’s create an infinite sequence. We can do this easily using something like ‘cycle’ which takes some data structure and returns an infinitely repeating sequence of the given values. For example:

user=> (take 5 (cycle [1 2 ]))
(1 2 1 2 1)

If we don’t ‘take’ just a few elements, this will repeat forever. Let’s check what we assume is the case:

user=> (seq?(cycle [2 3 ]))
true

What do you know? ‘cycle’ returns a seq. In the above sample, the cycle is obviously not fully computed before we ‘take’ our result, otherwise it would never finish. So cycle returns a lazy seq.

Later on I’m going to explore laziness in Clojure and expand on some of these observations.

Written by newcome

February 11, 2012 at 4:59 pm

Posted in Uncategorized

One Response

Subscribe to comments with RSS.

  1. Hi there, i read your blog occasionally and i own a similar one and i was just curious if you get a
    lot of spam remarks? If so how do you protect against it, any plugin or
    anything you can suggest? I get so much lately
    it’s driving me insane so any support is very much appreciated.

    folija

    February 25, 2013 at 2:54 pm


Leave a comment