emacs-devel
[Top][All Lists]
Advanced

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

`thunk-let'?


From: Michael Heerdegen
Subject: `thunk-let'?
Date: Sun, 08 Oct 2017 22:12:20 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.60 (gnu/linux)

Hi,

I often want thunks (like in "thunk.el") when I'm not sure if it's
necessary to do some calculation (e.g. the result is used in an `if'
branch) and want to save the time if not.  So I bind a variable not to
the expression to eval but to a thunk evaluating it, and use
`thunk-force' everywhere I need to refer to the value.

It would be cool if the programmer wouldn't need to speak that out, if
you could hide away the details and use normal variables instead.  You
would have a kind of `let' that would look like making bindings to
variables just like `let' but the expressions that are assigned are not
evaluated before they are needed - maybe never, but also maximally once.

I then realized that doing this should be trivial thanks to
`cl-symbol-macrolet': instead of binding the original variables, you
bind the expressions - wrapped inside `thunk-delay' - to helper vars.
Then, you `symbol-macrolet' all original variables to a `thunk-force' of
the according helper variable:

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

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

(defmacro thunk-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 (thunk-delay ,binding)))
          bindings)
     (cl-symbol-macrolet
         ,(mapcar (pcase-lambda (`(,thunk-var ,var ,_binding))
                    `(,var (thunk-force ,thunk-var)))
                  bindings)
       ,@body)))

(defmacro thunk-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)
      `(thunk-let (,(car bindings))
         (thunk-let ,(cdr bindings)
           ,@body))
    `(thunk-let ,bindings ,@body)))
#+end_src

An alternative name would be `delayed-let'.  I think it would be very
convenient.


Here is a playground example to test when and if something is calculated
(you need lexical-binding mode):

#+begin_src emacs-lisp
(defmacro calculate-with-message (varname expression)
  `(progn (message "Calculating %s..." ,varname)
          (sit-for 2)
          (prog1 ,expression
            (message "Calculating %s...done" ,varname)
            (sit-for 1))))

(thunk-let ((a (calculate-with-message "a" (+ 1 2)))
            (b (calculate-with-message "b" (* 10 3))))
  (1+ b))
;; etc.
#+end_src



What do people think about this idea?


Thanks,

Michael.



reply via email to

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