Note: There are a number of things that may be unfamiliar to new cljs devs. Rather than making this a huge post, I’ll write a separate post for each concept. For example, if the
lazy-catused in this post is unclear, check out my explanation of lazy-seq and recursion.
In my last post, we had some code that could read files line by line. We then used each line, either by a callback or a channel. While the code worked, I didn’t want the asynchronous reads. Instead, I wanted to get as close to this as possible:
This has the following benefits:
- It’s exactly the same as clj
- I don’t have to think about async code that provides no benefits here
- It lazily reads in the file and handles cleanup
- It’s a sequence and I can program to that interface
We can now read an arbitrary amount of data from the file, but we have to manage finding actual lines of text. Here’s the logic we need to implement:
- Get the next chunk of data
- If it contains a line of text, return that line of text and save the remaining text for the next call
- If it does not contain a line of text, get more data and try our line check again
- If we read the EOL, just return whatever data we have
Here’s what I ended up with:
line-seq takes a file descriptor and lazily reads from the file, returning a lazy sequence of lines.
The last bit is our
with-open macro. Note: cljs macros have to be written in clojure
Now, we’re where we want to be:
And since we have a
seq, we can use all of our normal tools:
Boom. Pretty sweet.
Here’s a gist of the full code.