最近、読んだ「Land of Lisp」の中に遅延プログラミングがでてきた。
 ポール・グラハムの「On Lisp」にもあったよなぁ1、とか、考えていてふと気づいた。レキシカルスコープがサポートされたんだからelispでも可能なんじゃね?
 試してみた2。
;; -*- lexical-binding: t; -*-
(defmacro lazy(body)
  (let ((forced (gensym))
 (value (gensym)))
    `(let ((,forced nil)
   (,value nil))
      (lambda()
 (unless ,forced
   (setf ,value (progn ,body))
   (setf ,forced t))
 ,value))))
(defun force(lazy-value)
  (funcall lazy-value))
(defmacro lazy-cons(a d)
  `(lazy (cons ,a ,d)))
(defun lazy-car(x)
  (car (force x)))
(defun lazy-cdr(x)
  (cdr (force x)))
(defun lazy-nil()
  (lazy nil))
(defun lazy-null(x)
  (null (force x)))
(defun make-lazy(lst)
  (lazy (when lst
   (cons (car lst)(make-lazy (cdr lst))))))
(defun take(n lst)
  (unless (or (zerop n)(lazy-null lst))
    (cons (lazy-car lst)(take (1- n)(lazy-cdr lst)))))
(defun take-all(lst)
  (unless (lazy-null lst)
    (cons (lazy-car lst)(take-all (lazy-cdr lst)))))
 いちおう動いているみたいだ。
 注意。「lazy」はレキシカルスコープの環境で動かすこと。考えてみれば、当たり前なんだけど、最初、うまく動かなくて考えこんでしまった。lazyの定義はレキシカルスコープでやって、実行をダイナミックスコープ環境でやっていたので。closureになってなかった。
 それにしてもcl.elでlabelsも使えるんだ。知らんかったわ。
;; -*- lexical-binding: t; -*-
(setq wrk (labels ((f (n)
        (lazy-cons n (f (1+ n)))))
     (f 1)))
(take 10 wrk)