summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Elsing <david.elsing@posteo.net>2025-03-04 20:33:08 +0000
committerLudovic Courtès <ludo@gnu.org>2025-03-05 00:28:49 +0100
commit30e51cb6b42e86f9f94d6380f69a1020ee99ff39 (patch)
tree6bcf2847774381f7c3a102c1a0ae143ee97d9879
parent749eb1a2dd9fdf63a71f223b3f6756d9cb5940e6 (diff)
gexp: ‘with-parameters’ properly handles ‘%graft?’.
Fixes <https://issues.guix.gnu.org/75879>. * .dir-locals.el (scheme-mode): Remove mparameterize indentation rules. Add state-parameterize and store-parameterize indentation rules. * etc/manifests/system-tests.scm (test-for-current-guix): Replace mparameterize with store-parameterize. * etc/manifests/time-travel.scm (guix-instance-compiler): Likewise. * gnu/tests.scm (compile-system-test): Likewise. * guix/gexp.scm (compile-parameterized): Use state-call-with-parameters. * guix/monads.scm (mparameterize): Remove macro. (state-call-with-parameters): New procedure. (state-parameterize): New macro. * guix/store.scm (store-parameterize): New macro. * tests/gexp.scm ("with-parameters for %graft?"): New test. * tests/monads.scm ("mparameterize"): Remove test. ("state-parameterize"): New test. Co-authored-by: Ludovic Courtès <ludo@gnu.org> Change-Id: I0c74066ca3f37072815b073fb3039925488a9645 Signed-off-by: Ludovic Courtès <ludo@gnu.org>
-rw-r--r--.dir-locals.el3
-rw-r--r--etc/manifests/system-tests.scm2
-rw-r--r--etc/manifests/time-travel.scm8
-rw-r--r--gnu/tests.scm8
-rw-r--r--guix/gexp.scm42
-rw-r--r--guix/monads.scm68
-rw-r--r--guix/store.scm2
-rw-r--r--tests/gexp.scm20
-rw-r--r--tests/monads.scm20
9 files changed, 114 insertions, 59 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index d629b51c8a1..76c9e12992a 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -138,7 +138,8 @@
138 (eval . (put 'munless 'scheme-indent-function 1)) 138 (eval . (put 'munless 'scheme-indent-function 1))
139 (eval . (put 'mlet* 'scheme-indent-function 2)) 139 (eval . (put 'mlet* 'scheme-indent-function 2))
140 (eval . (put 'mlet 'scheme-indent-function 2)) 140 (eval . (put 'mlet 'scheme-indent-function 2))
141 (eval . (put 'mparameterize 'scheme-indent-function 2)) 141 (eval . (put 'state-parameterize 'scheme-indent-function 2))
142 (eval . (put 'store-parameterize 'scheme-indent-function 2))
142 (eval . (put 'run-with-store 'scheme-indent-function 1)) 143 (eval . (put 'run-with-store 'scheme-indent-function 1))
143 (eval . (put 'run-with-state 'scheme-indent-function 1)) 144 (eval . (put 'run-with-state 'scheme-indent-function 1))
144 (eval . (put 'wrap-program 'scheme-indent-function 1)) 145 (eval . (put 'wrap-program 'scheme-indent-function 1))
diff --git a/etc/manifests/system-tests.scm b/etc/manifests/system-tests.scm
index 4e16c53dcfc..430f5075207 100644
--- a/etc/manifests/system-tests.scm
+++ b/etc/manifests/system-tests.scm
@@ -53,7 +53,7 @@ instance."
53 (map (lambda (test) 53 (map (lambda (test)
54 (system-test 54 (system-test
55 (inherit test) 55 (inherit test)
56 (value (mparameterize %store-monad ((current-guix-package guix)) 56 (value (store-parameterize ((current-guix-package guix))
57 (system-test-value test))))) 57 (system-test-value test)))))
58 (match (getenv "TESTS") 58 (match (getenv "TESTS")
59 (#f 59 (#f
diff --git a/etc/manifests/time-travel.scm b/etc/manifests/time-travel.scm
index 039ca89889d..5256d2195c2 100644
--- a/etc/manifests/time-travel.scm
+++ b/etc/manifests/time-travel.scm
@@ -22,7 +22,7 @@
22(use-modules (srfi srfi-9) (ice-9 match) 22(use-modules (srfi srfi-9) (ice-9 match)
23 (guix channels) (guix gexp) 23 (guix channels) (guix gexp)
24 ((guix store) #:select (%store-monad)) 24 ((guix store) #:select (%store-monad))
25 ((guix monads) #:select (mparameterize return)) 25 ((guix monads) #:select (store-parameterize return))
26 ((guix git) #:select (%repository-cache-directory)) 26 ((guix git) #:select (%repository-cache-directory))
27 ((guix build utils) #:select (mkdir-p))) 27 ((guix build utils) #:select (mkdir-p)))
28 28
@@ -40,9 +40,9 @@
40 ;; When this manifest is evaluated by Cuirass, make sure it does not 40 ;; When this manifest is evaluated by Cuirass, make sure it does not
41 ;; fiddle with the cached checkout that Cuirass is also using since 41 ;; fiddle with the cached checkout that Cuirass is also using since
42 ;; concurrent accesses are unsafe. 42 ;; concurrent accesses are unsafe.
43 (mparameterize %store-monad ((%repository-cache-directory 43 (store-parameterize ((%repository-cache-directory
44 (string-append (%repository-cache-directory) 44 (string-append (%repository-cache-directory)
45 "/time-travel/" system))) 45 "/time-travel/" system)))
46 (return (mkdir-p (%repository-cache-directory))) 46 (return (mkdir-p (%repository-cache-directory)))
47 (latest-channel-derivation channels))))) 47 (latest-channel-derivation channels)))))
48 48
diff --git a/gnu/tests.scm b/gnu/tests.scm
index 2a9e51511f0..1e3dbf09445 100644
--- a/gnu/tests.scm
+++ b/gnu/tests.scm
@@ -34,7 +34,7 @@
34 #:use-module (gnu services shepherd) 34 #:use-module (gnu services shepherd)
35 #:use-module (guix discovery) 35 #:use-module (guix discovery)
36 #:use-module (guix monads) 36 #:use-module (guix monads)
37 #:use-module ((guix store) #:select (%store-monad)) 37 #:use-module ((guix store) #:select (%store-monad store-parameterize))
38 #:use-module ((guix utils) 38 #:use-module ((guix utils)
39 #:select (%current-system %current-target-system)) 39 #:select (%current-system %current-target-system))
40 #:use-module (srfi srfi-1) 40 #:use-module (srfi srfi-1)
@@ -289,9 +289,9 @@ the system under test."
289(define-gexp-compiler (compile-system-test (test <system-test>) 289(define-gexp-compiler (compile-system-test (test <system-test>)
290 system target) 290 system target)
291 "Compile TEST to a derivation." 291 "Compile TEST to a derivation."
292 (mparameterize %store-monad ((%current-system system) 292 (store-parameterize ((%current-system system)
293 (%current-target-system target)) 293 (%current-target-system target))
294 (system-test-value test))) 294 (system-test-value test)))
295 295
296(define (test-modules) 296(define (test-modules)
297 "Return the list of modules that define system tests." 297 "Return the list of modules that define system tests."
diff --git a/guix/gexp.scm b/guix/gexp.scm
index ad51bc55b78..9ce6810172b 100644
--- a/guix/gexp.scm
+++ b/guix/gexp.scm
@@ -733,26 +733,28 @@ x86_64-linux when COREUTILS is lowered."
733 (lambda (parameterized system target) 733 (lambda (parameterized system target)
734 (match (parameterized-bindings parameterized) 734 (match (parameterized-bindings parameterized)
735 (((parameters values) ...) 735 (((parameters values) ...)
736 (let ((fluids (map parameter-fluid parameters)) 736 (let ((thunk (parameterized-thunk parameterized))
737 (thunk (parameterized-thunk parameterized))) 737 (values (map (lambda (thunk) (thunk)) values)))
738 ;; Install the PARAMETERS for the dynamic extent of THUNK. 738 ;; Install the PARAMETERS for the store monad.
739 (with-fluids* fluids 739 (state-with-parameters parameters values
740 (map (lambda (thunk) (thunk)) values) 740 ;; Install the PARAMETERS for the dynamic extent of THUNK.
741 (lambda () 741 ;; Special-case '%current-system' and '%current-target-system' to
742 ;; Special-case '%current-system' and '%current-target-system' to 742 ;; make sure we get the desired effect.
743 ;; make sure we get the desired effect. 743 (with-fluids* (map parameter-fluid parameters)
744 (let ((system (if (memq %current-system parameters) 744 values
745 (%current-system) 745 (lambda ()
746 system)) 746 (let ((system (if (memq %current-system parameters)
747 (target (if (memq %current-target-system parameters) 747 (%current-system)
748 (%current-target-system) 748 system))
749 target))) 749 (target (if (memq %current-target-system parameters)
750 (match (thunk) 750 (%current-target-system)
751 ((? struct? obj) 751 target)))
752 (lower-object obj system #:target target)) 752 (match (thunk)
753 (obj ;store item 753 ((? struct? obj)
754 (with-monad %store-monad 754 (lower-object obj system #:target target))
755 (return obj))))))))))) 755 (obj ;store item
756 (with-monad %store-monad
757 (return obj))))))))))))
756 758
757 expander => (lambda (parameterized lowered output) 759 expander => (lambda (parameterized lowered output)
758 (match (parameterized-bindings parameterized) 760 (match (parameterized-bindings parameterized)
diff --git a/guix/monads.scm b/guix/monads.scm
index 0bd8ac9315d..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
3;;; Copyright © 2025 David Elsing <david.elsing@posteo.net>
3;;; 4;;;
4;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
5;;; 6;;;
@@ -19,6 +20,7 @@
19(define-module (guix monads) 20(define-module (guix monads)
20 #:use-module ((system syntax) 21 #:use-module ((system syntax)
21 #:select (syntax-local-binding)) 22 #:select (syntax-local-binding))
23 #:autoload (guix deprecation) (warn-about-deprecation)
22 #:use-module (ice-9 match) 24 #:use-module (ice-9 match)
23 #:use-module (srfi srfi-1) 25 #:use-module (srfi srfi-1)
24 #:use-module (srfi srfi-9) 26 #:use-module (srfi srfi-9)
@@ -40,7 +42,6 @@
40 mbegin 42 mbegin
41 mwhen 43 mwhen
42 munless 44 munless
43 mparameterize
44 lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift 45 lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
45 listm 46 listm
46 foldm 47 foldm
@@ -58,7 +59,9 @@
58 set-current-state 59 set-current-state
59 state-push 60 state-push
60 state-pop 61 state-pop
61 run-with-state)) 62 run-with-state
63 state-parameterize
64 mparameterize))
62 65
63;;; Commentary: 66;;; Commentary:
64;;; 67;;;
@@ -399,21 +402,6 @@ expression."
399 (mbegin %current-monad 402 (mbegin %current-monad
400 mexp0 mexp* ...))))) 403 mexp0 mexp* ...)))))
401 404
402(define-syntax mparameterize
403 (syntax-rules ()
404 "This form implements dynamic scoping, similar to 'parameterize', but in a
405monadic context."
406 ((_ monad ((parameter value) rest ...) body ...)
407 (let ((old-value (parameter)))
408 (mbegin monad
409 ;; XXX: Non-local exits are not correctly handled.
410 (return (parameter value))
411 (mlet monad ((result (mparameterize monad (rest ...) body ...)))
412 (parameter old-value)
413 (return result)))))
414 ((_ monad () body ...)
415 (mbegin monad body ...))))
416
417(define-syntax define-lift 405(define-syntax define-lift
418 (syntax-rules () 406 (syntax-rules ()
419 ((_ liftn (args ...)) 407 ((_ liftn (args ...))
@@ -600,4 +588,48 @@ the previous state as a monadic value."
600 (lambda (state) 588 (lambda (state)
601 (values state (cons value state)))) 589 (values state (cons value state))))
602 590
591(define-public (state-with-parameters parameters parameter-values mval)
592 "Set PARAMETERS to PARAMETER-VALUES for the dynamic extent of MVAL, a value
593in the state monad."
594 (define (set-value parameter value)
595 (parameter value))
596
597 (lambda (state)
598 ;; XXX: 'with-fluids*' does not work with prompts, therefore the parameters
599 ;; are set globally. This leaves the parameters changed upon a non-local
600 ;; exit and restores them only after running MVAL to completion. See
601 ;; <https://issues.guix.gnu.org/76485>.
602 (let ((old-values (map set-value parameters parameter-values)))
603 (call-with-values
604 (lambda ()
605 (mval state))
606 (lambda (value state)
607 (map set-value parameters old-values)
608 (values value state))))))
609
610(define-syntax state-parameterize
611 (syntax-rules ()
612 "This form implements dynamic scoping, similar to 'parameterize', but also
613in the monadic context of the state monad."
614 ((_ ((param value) ...) body ...)
615 (let ((parameters (list param ...))
616 (values (list value ...)))
617 (state-with-parameters parameters values
618 ;; Install the parameters also for the evaluation of body ...
619 (with-fluids* (map parameter-fluid parameters)
620 values
621 (lambda ()
622 (mbegin %state-monad body ...))))))))
623
624(define-syntax mparameterize ;can be removed after 2026-03-05
625 (lambda (s)
626 "This is the old form for 'state-parameterize', which pretended to work
627with any monad but was in fact specialized for '%state-monad'."
628 (syntax-case s ()
629 ((_ monad bindings body ...)
630 (begin
631 (warn-about-deprecation 'mparameterize (current-source-location)
632 #:replacement 'state-parameterize)
633 #'(state-parameterize bindings body ...))))))
634
603;;; monads.scm end here 635;;; monads.scm end here
diff --git a/guix/store.scm b/guix/store.scm
index cf5848e580b..bae8e7762b0 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -178,6 +178,7 @@
178 store-lift 178 store-lift
179 store-lower 179 store-lower
180 run-with-store 180 run-with-store
181 store-parameterize
181 %guile-for-build 182 %guile-for-build
182 current-system 183 current-system
183 set-current-system 184 set-current-system
@@ -1919,6 +1920,7 @@ This is a mutating version that should be avoided. Prefer the functional
1919(define-alias %store-monad %state-monad) 1920(define-alias %store-monad %state-monad)
1920(define-alias store-return state-return) 1921(define-alias store-return state-return)
1921(define-alias store-bind state-bind) 1922(define-alias store-bind state-bind)
1923(define-alias store-parameterize state-parameterize)
1922 1924
1923;; Instantiate templates for %STORE-MONAD since it's syntactically different 1925;; Instantiate templates for %STORE-MONAD since it's syntactically different
1924;; from %STATE-MONAD. 1926;; from %STATE-MONAD.
diff --git a/tests/gexp.scm b/tests/gexp.scm
index e870f6cb1b9..2376c70d1ba 100644
--- a/tests/gexp.scm
+++ b/tests/gexp.scm
@@ -451,6 +451,26 @@
451 (return (string=? (derivation-file-name drv) 451 (return (string=? (derivation-file-name drv)
452 (derivation-file-name result))))) 452 (derivation-file-name result)))))
453 453
454(test-assertm "with-parameters for %graft?"
455 (mlet* %store-monad ((replacement -> (package
456 (inherit %bootstrap-guile)
457 (name (string-upcase
458 (package-name
459 %bootstrap-guile)))))
460 (guile -> (package
461 (inherit %bootstrap-guile)
462 (replacement replacement)))
463 (drv0 (package->derivation %bootstrap-guile))
464 (drv1 (package->derivation replacement))
465 (obj0 -> (with-parameters ((%graft? #f))
466 guile))
467 (obj1 -> (with-parameters ((%graft? #t))
468 guile))
469 (result0 (lower-object obj0))
470 (result1 (lower-object obj1)))
471 (return (and (eq? drv0 result0)
472 (eq? drv1 result1)))))
473
454(test-assert "with-parameters + file-append" 474(test-assert "with-parameters + file-append"
455 (let* ((system (match (%current-system) 475 (let* ((system (match (%current-system)
456 ("aarch64-linux" "x86_64-linux") 476 ("aarch64-linux" "x86_64-linux")
diff --git a/tests/monads.scm b/tests/monads.scm
index 7f255f02bf5..c05d13776a5 100644
--- a/tests/monads.scm
+++ b/tests/monads.scm
@@ -136,18 +136,16 @@
136 %monads 136 %monads
137 %monad-run)) 137 %monad-run))
138 138
139(test-assert "mparameterize" 139(test-assert "state-parameterize"
140 (let ((parameter (make-parameter 'outside))) 140 (let ((parameter (make-parameter 'outside)))
141 (every (lambda (monad run) 141 (equal?
142 (equal? 142 (run-with-state
143 (run (mlet monad ((outer (return (parameter))) 143 (mlet %state-monad ((outer (return (parameter)))
144 (inner 144 (inner
145 (mparameterize monad ((parameter 'inside)) 145 (state-parameterize ((parameter 'inside))
146 (return (parameter))))) 146 (return (parameter)))))
147 (return (list outer inner (parameter))))) 147 (return (list outer inner (parameter)))))
148 '(outside inside outside))) 148 '(outside inside outside))))
149 %monads
150 %monad-run)))
151 149
152(test-assert "mlet* + text-file + package-file" 150(test-assert "mlet* + text-file + package-file"
153 (run-with-store %store 151 (run-with-store %store