[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Some macros to make package definitions prettier
From: |
Thompson, David |
Subject: |
Re: Some macros to make package definitions prettier |
Date: |
Wed, 25 Feb 2015 12:04:31 -0500 |
On Wed, Feb 25, 2015 at 11:42 AM, Taylan Ulrich Bayırlı/Kammer
<address@hidden> wrote:
> I would propose the following macros to make package definitions
> somewhat nicer:
I've been thinking that we need some macros for this for awhile!
Thanks for getting the conversation started.
>
> ;;; modify-phases
>
> (define-syntax modify-phases
> (syntax-rules ()
> ((modify-phases phases mod-spec ...)
> (let* ((phases* phases)
> (phases* (%modify-phases phases* mod-spec))
> ...)
> phases*))))
>
> (define-syntax %modify-phases
> (syntax-rules (delete replace add-before add-after)
> ((_ phases (delete old-phase-name))
> (alist-delete 'old-phase-name phases))
> ((_ phases (replace old-phase-name new-phase))
> (alist-replace 'old-phase-name new-phase phases))
> ((_ phases (add-before old-phase-name new-phase-name new-phase))
> (alist-cons-before 'old-phase-name 'new-phase-name new-phase phases))
> ((_ phases (add-after old-phase-name new-phase-name new-phase))
> (alist-cons-after 'old-phase-name 'new-phase-name new-phase phases))))
>
> ;;; Usage example:
>
> (modify-phases '((foo . 0) (bar . 1) (baz . 2))
> (delete foo)
> (replace bar 'x)
> (add-before baz pre-baz 'y)) ;=> ((bar . x) (pre-baz . y) (baz . 2))
>
>
> This has the following advantages:
>
> - The order in which the phases are modified is top-down, where in our
> current style it's bottom-up which both distracts (IMO), and one may
> forget, as the chain grows and one forgets that it's indeed just a
> chain of function calls like (foo (bar (baz x))).
>
> - Indentation doesn't keep growing as one adds more modifications.
>
> - It's easier for Scheme-newcomers, though one might say alists are
> pretty simple and fundamental and should be learned immediately...
I like the top-down ordering and the linear structure rather than a
nested structure like we have currently.
I understand the temptation to name it 'modify-phases', but since this
macro applies to any alist, I think 'modify-alist' or something with
"alist" in the name would be more appropriate.
>
> And secondly:
>
> ;;; phase-lambda
>
> (define-syntax phase-lambda
> (syntax-rules ()
> ((phase-lambda ((arg (alist-entry ...))
> ...)
> body ...)
> (lambda* (#:key arg ... #:allow-other-keys)
> (let-values (((alist-entry ...)
> (let ((arg* arg))
> (values
> (assoc-ref arg* (symbol->string 'alist-entry))
> ...)))
> ...)
> body ...)))))
>
> ;;; Usage example:
>
> (phase-lambda ((inputs (libx liby))
> (outputs (out)))
> ...)
>
> ;;; effectively equivalent to:
>
> (lambda* (#:key inputs outputs #:allow-other-keys)
> (let ((libx (assoc-ref inputs "libx"))
> (liby (assoc-ref inputs "liby"))
> (out (assoc-ref outputs "out")))
> ...))
>
>
> This saves the usual boilerplate of '(#:key inputs outputs
> #:allow-other-keys)' and the subsequent `assoc-ref' uses. One might say
> it's too specific because the phase procedures also receive non-alist
> arguments (although one can add an argument clause like '(foo ())' and
> it will work because no `assoc-ref' call will happen on `foo', though
> that's a hack), but I looked at all occurrences of "#:key" in
> gnu/packages/*.scm, and pure uses of `input' and/or `output' are
> extremely dominant, such that it should be fine to fall back to a plain
> `lambda*' in the remaining cases.
I'm not sure I like combining the alist destructuring part of this
macro. IMO, 'phase-lambda' could just take care of the 'lambda*'
boilerplate, and maybe another macro ('alist-let'?) to reduce the
'assoc-ref' usage.
Implementation details aside, I think macros like these are very much
needed to reduce boilerplate and improve the readability of our
package recipes.
Thanks for sharing!