Yesterday i ran into this pipes library for common lisp. It looks to some extent quite like clojure's lazy sequences abstraction, so i decided using it to implement the classic (and classy) clojure example of recursive lazy Fibonacci sequence definition in Common Lisp (for purely educational purpose).
that is what it looks like in clojure:
(def fibs (lazy-cat [0 1] (map +' fibs (rest fibs))))
(nth fibs 100)
;;=> 354224848179261915075N
that is quite simple, but the problem is that it keeps possibly huge lazy sequence in a global scope forever, so with some hacks i rewrote it so it can be used inside let binding:
(let [f (memoize (fn [f]
(lazy-cat [0 1]
(let [data (f f)]
(map +' data (rest data))))))
fibs (f f)]
(nth fibs 100))
;;=> 354224848179261915075N
the whole memoize and (f f) thing is to emulate data recursion in let.
then i've implemented it using the same approach in CL.
first, some utilities:
;; analogue of `list*` for pipes
(defmacro make-pipe* (x1 &rest xs)
(if xs
`(pipes:make-pipe ,x1 (make-pipe* ,@xs))
x1))
;; wraps function so that it always returns the result of its first invocation
(defun once (f)
(let ((called (cons nil nil)))
(lambda (&rest args)
(if (car called)
(cdr called)
(let ((res (apply f args)))
(setf called (cons t res))
res)))))
;; map over two pipes
(defun pipe-map2 (fn pipe1 pipe2)
(if (or (eq pipe1 pipes:+empty-pipe+)
(eq pipe2 pipes:+empty-pipe+))
pipes:+empty-pipe+
(pipes:make-pipe (funcall fn (pipes:pipe-head pipe1) (pipes:pipe-head pipe2))
(pipe-map2 fn (pipes:pipe-tail pipe1) (pipes:pipe-tail pipe2)))))
and then here goes the actual implementation:
(let* ((f (once (lambda (f)
(make-pipe* 0 1
(let ((data (funcall f f)))
(pipe-map2 #'+ data (pipes:pipe-tail data)))))))
(fibs (funcall f f)))
(pipes:pipe-values fibs 10))
;;=> (0 1 1 2 3 5 8 13 21 34 55 . #<CLOSURE (LAMBDA () :IN PIPE-MAP2) {10096C6BBB}>)
ok. it works. But the question is: as common lisp provides much more metaprogramming and compilation control utilities than clojure has, are there any proper ones that could make "self recursive let" (as i call it) more elegant, eliminating the need for ugly hack with memoized function calls, preferably avoiding mutable state (though i'm not sure it is possible at all)?
cdrof the last cons cell, so there was no problem having functions as the elements in a list) - Will Ness