Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add elpaca-thunk macro #173

Open
wants to merge 2 commits into
base: fix/thunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions doc/elpaca-thunk.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
;;; Example use of `elpaca-thunk' -*- lexical-binding: t; -*-

;; Assuming that Elpaca has been bootstrapped
(require 'elpaca)

;; Compile-time dependency
(eval-when-compile
;; Built-in library
(require 'generator)
;; Macro definition
(defmacro noop-macro (&rest body)
"Do nothing to BODY."
(declare (indent 0))
(macroexp-progn body))
;; Inline function definition
(defsubst noop-fn (arg)
"Do nothing to ARG."
arg))

;; Unlike `elpaca', `elpaca-thunk' wraps BODY directly in a thunk
;; (nullary function), which means macros and inline functions are
;; expanded at compile time. Conversely, this also means that
;; bootstrapping can create dependency problem. Don't use it unless
;; you're sure what you're doing!
(elpaca-thunk nil
;; This shouldn't lead to void-function
(noop-macro
(message (noop-fn "first"))
(noop-fn (iter-lambda () nil))
(message (noop-fn "second"))))

;; Process the above order
(elpaca-process-queues)

;; Local Variables:
;; no-native-compile: t
;; no-update-autoloads: t
;; End:
36 changes: 27 additions & 9 deletions elpaca.el
Original file line number Diff line number Diff line change
Expand Up @@ -1450,20 +1450,21 @@ When MESSAGE is non-nil, message the list of dependents."
"Debounces interactive evaluation of multiple `elpaca' forms.")

;;;; COMMANDS/MACROS
;;;###autoload
(defmacro elpaca (order &rest body)
"Queue ORDER for installation/activation, defer execution of BODY.
If ORDER is `nil`, defer BODY until orders have been processed."
(declare (indent 1) (debug t))
(defun elpaca--expand (order body make-thunk)
"Expand `elpaca' and alike.
ORDER and BODY are as in `elpaca', while MAKE-THUNK is used to produce
the thunk when given BODY."
(declare (side-effect-free t))
(let ((o (gensym "order-")) (item (gensym "item-")) (q (gensym "q-")))
`(let* ((,o ,@(if (memq (car-safe order) '(quote \`)) `(,order) `(',order)))
(,item (elpaca--first ,o))
(,q (or (and after-init-time (elpaca--q (elpaca-get ,item))) (car elpaca--queues))))
,@(when body
`((if ,item
(setf (alist-get ,item (elpaca-q<-forms ,q)) (lambda () (eval '(progn ,@body) t)))
;;@FIX: nil semantics not good for multiple deferred...
(push (cons ,item (lambda () (eval '(progn ,@body) t))) (elpaca-q<-forms ,q)))))
(let ((thunk (funcall make-thunk body)))
`((if ,item
(setf (alist-get ,item (elpaca-q<-forms ,q)) ,thunk)
;;@FIX: nil semantics not good for multiple deferred...
(push (cons ,item ,thunk) (elpaca-q<-forms ,q))))))
(when ,o (elpaca--queue ,o ,q))
(when after-init-time
(when-let ((e (elpaca-get ,item)))
Expand All @@ -1476,6 +1477,23 @@ If ORDER is `nil`, defer BODY until orders have been processed."
(run-at-time elpaca-interactive-interval nil #'elpaca-process-queues)))
nil)))

;;;###autoload
(defmacro elpaca (order &rest body)
"Queue ORDER for installation/activation, defer execution of BODY.
If ORDER is `nil`, defer BODY until orders have been processed."
(declare (indent 1) (debug t))
(elpaca--expand
order body
(lambda (body) `(lambda () (eval '(progn ,@body) t)))))

;;;###autoload
(defmacro elpaca-thunk (order &rest body)
"Queue ORDER like `elpaca', but BODY is wrapped in a thunk."
(declare (indent 1) (debug t))
(elpaca--expand
order body
(lambda (body) `(lambda () ,@body))))

(defcustom elpaca-wait-interval 0.01 "Seconds between `elpaca-wait' status checks."
:type 'number)

Expand Down