radian-software / straight.el

🍀 Next-generation, purely functional package manager for the Emacs hacker.
MIT License
2.69k stars 150 forks source link

Using straight.el conditionally from use-package? #1142

Closed CyberShadow closed 6 months ago

CyberShadow commented 6 months ago

Hi, sorry, I couldn't figure out how to do this or if it is even possible.

I would like to do something like the following:

(use-package flycheck
  :straight (some condition here which returns t or nil)
  ...

The motivation being that I'm trying to gradually migrate to a setup where some packages are provided via another mechanism (Nix).

Thanks!

progfolio commented 6 months ago

use-package is a macro, so it's arguments aren't evaluated. You could use eval to do something like:

(eval `(use-package example :straight ,(your condition here)) t)
CyberShadow commented 6 months ago

That means I would have to wrap all my use-package invocations in eval. Is there a better way?

progfolio commented 6 months ago

You could write a macro which expands to the appropriate use-package declaration. That's not really straight.el specific, though.

CyberShadow commented 6 months ago

Would it make sense for straight to recognize a special form, such as (eval ...), which would allow something like that without something as radical as wrapping the entire use-package invocation in a custom macro?

CyberShadow commented 6 months ago

You could write a macro which expands to the appropriate use-package declaration. That's not really straight.el specific, though.

Here it is:

;; https://github.com/radian-software/straight.el/issues/1142
(defmacro cs/use-package (&rest forms)
  "A wrapper around `use-package' which allows using , to
evaluate Lisp expressions anywhere."
  (declare (indent defun))
  (let* (visit ; silence byte-compiler
         (visit (lambda (form)
                  (cond
                   ((and (consp form)
                         (consp (cdr form))
                         (null (cddr form)) ; two-element list
                         (eq (car form) '\`))
                    form)
                   ((and (consp form)
                         (consp (cdr form))
                         (null (cddr form)) ; two-element list
                         (eq (car form) '\,))
                    (eval (cadr form)))
                   ((consp form)
                    (cons (funcall visit (car form))
                          (funcall visit (cdr form))))
                   (t
                    form)))))
    `(use-package ,@(funcall visit forms))))

Using , (while not in a `) will now make it evaluate the expression, as if the entire call was wrapped in a `( ... ).

It still doesn't seem right to me that this should be necessary. If I can't put a boolean expression somewhere where t is accepted, I think something has gone very wrong - though, I don't know if the problem lies with straight.el or use-package.

progfolio commented 6 months ago

Here it is:

Here's a modified version which should (untested) work the same.

(defun cs/visit (form)
  (cond
   ((memq (car-safe form) '(quote \,)) (eval (cadr form)))
   ((consp form) (cons (cs/visit (car form)) (cs/visit (cdr form))))
   (t form)))

(defmacro cs/use-package (&rest forms)
  "A wrapper around `use-package' which allows using , to
evaluate Lisp expressions anywhere."
  (declare (indent defun))
  `(use-package ,@(cs/visit forms)))

If I can't put a boolean expression somewhere where t is accepted, I think something has gone very wrong - though, I don't know if the problem lies with straight.el or use-package.

Use-package offers a domain specific language with its own syntax rules. Consider :defer, :demand, :ensure, :no-require, which also accept boolean values, but not expressions. This was likely a design decision to keep things simple.

It still doesn't seem right to me that this should be necessary.

Use-package's manual mentions a similar use-case to what you're after. See the "Making conditional loading affect :preface and :ensure" section:

https://www.gnu.org/software/emacs/manual/html_node/use-package/Conditional-loading.html

The advice given is to wrap the entire declaration form.

Would it make sense for straight to recognize a special form, such as (eval ...),

I don't think so. This would complicate straight's use-package integration for an uncommon case which can be dealt with outside of straight. Consider that the suggestion would make it impossible to distinguish between a recipe for a package called "eval" and the special form. It would also be an outlier in terms of other use-package keywords mentioned above. Any thoughts on this, @raxod502?

CyberShadow commented 6 months ago

Here's a modified version which should (untested) work the same.

Nice. After posting I realized that the implementation can also be reduced to simply (eval (list '\` (cons 'use-package forms))).

Consider :defer, :demand, :ensure, :no-require, which also accept boolean values, but not expressions.

Good point; perhaps this is better addressed by a change in use-package then, not straight.

Use-package's manual mentions a similar use-case to what you're after.

Thank you. The goal here is not to prevent the package from loading, though; it is to affect where it is loaded from.

Consider that the suggestion would make it impossible to distinguish between a recipe for a package called "eval" and the special form.

Good point.

Not that it is directly related, but I noticed that the use-package manual mentions an :eval syntax for another integration (delight).

One universal fix would be to make use-package interpolate commas appearing at the top level, like the macro we discussed. This would be a purely additive change because commas currently are not valid syntax at the top level. It would not be very discoverable, though.

progfolio commented 6 months ago

Vladimir Panteleev @.***> writes:

One universal fix would be to make use-package interpolate commas appearing at the top level, like the macro we discussed. This would be a purely additive change because commas currently are not valid syntax at the top level. It would not be very discoverable, though.

I'm sure the emacs-devel mailing list would be willing to discuss such a change. Give it a shot if you think it's a worthwhile addition to use-package.

raxod502 commented 6 months ago

Yes, I understand the use case, and I don't have any issue with it - but it should be done in a way that doesn't create complications for normal users. Given the sophistication of Emacs' macro system, I think the suggestion to create a custom macro is a reasonable one given the relative uncommonness of the situation. I do the same in my own configuration, to implement exactly the same conditional mechanism.