[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.