guix-devel
[Top][All Lists]
Advanced

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

Some macros to make package definitions prettier


From: Taylan Ulrich Bayırlı/Kammer
Subject: Some macros to make package definitions prettier
Date: Wed, 25 Feb 2015 17:42:23 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.4 (gnu/linux)

I would propose the following macros to make package definitions
somewhat nicer:


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


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.


WDYT?

Taylan



reply via email to

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