emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: `thunk-let'?


From: Michael Heerdegen
Subject: Re: `thunk-let'?
Date: Mon, 09 Oct 2017 16:07:33 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.60 (gnu/linux)

Michael Heerdegen <address@hidden> writes:

>  The second case should be trivial, that's what we get with
> `cl-symbol-macrolet' out of the box.

I tried the first way: I implemented a place `lazy-let--thunk-value'
that is only used internally, and went with `cl-symbol-macrolet'.

Now doing the e.g. the following works (lexical-binding needed!):

#+begin_src emacs-lisp
(lazy-let ((a (+ 1 2))
           (x (error "This evaluation would raise an error!"))
           (b (* 10 3))
           (c (* 10 10 10)))
  (setq a (+ b a)) ;  33
  (cl-incf b)      ;  31
  (+ a b c))

==> 1064
#+end_src

Here is the implementation:

#+begin_src emacs-lisp
;; -*- lexical-binding: t -*-

(eval-when-compile
  (require 'cl-lib))

(defun lazy-let--thunk-value (thunk) (funcall thunk))
(defun lazy-let--set-thunk-value (thunk value) (funcall thunk value))
(gv-define-simple-setter lazy-let--thunk-value lazy-let--set-thunk-value)

(defmacro lazy-let--make-thunk (expr)
  (let ((forced (make-symbol "forced"))
        (val    (make-symbol "val"))
        (args   (make-symbol "args")))
    `(let (,forced ,val)
       (lambda (&rest ,args)
         (if ,args
             (prog1 (setf ,val (car ,args))
               (setq ,forced t))
           (unless ,forced
             (setf ,val ,expr)
             (setf ,forced t))
           ,val)))))

(defmacro lazy-let (bindings &rest body)
  "Like `let' but make delayed bindings.
This is like `let' but all binding expressions are not calculated
before they are used."
  (declare (indent 1))
  (setq bindings
        (mapcar (pcase-lambda (`(,var ,binding))
                  (list (make-symbol (concat (symbol-name var) "_thunk"))
                        var binding))
                bindings))
  `(let ,(mapcar
          (pcase-lambda (`(,thunk-var ,_var ,binding))
            `(,thunk-var (lazy-let--make-thunk ,binding)))
          bindings)
     (cl-symbol-macrolet
         ,(mapcar (pcase-lambda (`(,thunk-var ,var ,_binding))
                    `(,var (lazy-let--thunk-value ,thunk-var)))
                  bindings)
       ,@body)))

(defmacro lazy-let* (bindings &rest body)
  "Like `let*' but make delayed bindings.
This is like `let*' but all binding expressions are not calculated
before they are used."
  (declare (indent 1))
  (if (> (length bindings) 1)
      `(lazy-let (,(car bindings))
         (lazy-let ,(cdr bindings)
           ,@body))
    `(lazy-let ,bindings ,@body)))


#+end_src


Regards,

Michael.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]