GNU bug report logs - #76485
[PATCH] gexp: ‘with-parameters’ properly handles ‘%graft?’.

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Sat, 22 Feb 2025 15:01:07 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 76485 in the body.
You can then email your comments to 76485 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix <at> cbaines.net, dev <at> jpoiret.xyz, ludo <at> gnu.org, othacehe <at> gnu.org, zimon.toutoune <at> gmail.com, me <at> tobias.gr, guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Sat, 22 Feb 2025 15:01:09 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix <at> cbaines.net, dev <at> jpoiret.xyz, ludo <at> gnu.org, othacehe <at> gnu.org, zimon.toutoune <at> gmail.com, me <at> tobias.gr, guix-patches <at> gnu.org. (Sat, 22 Feb 2025 15:01:09 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: David Elsing <david.elsing <at> posteo.net>,
 Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH] gexp: ‘with-parameters’ properly handles ‘%graft?’.
Date: Sat, 22 Feb 2025 15:59:37 +0100
Fixes <https://issues.guix.gnu.org/75879>.

* guix/gexp.scm (mcall-with-parameters): New procedure.
(compile-parameterized): Use it instead of ‘with-fluids’.
* tests/gexp.scm ("with-parameters for %graft?"): New test.

Reported-by: David Elsing <david.elsing <at> posteo.net>
Change-Id: Iddda7ead2aeef24dd989ac37a53fc99b726731b3
---
 guix/gexp.scm  | 31 ++++++++++++++++++++++++-------
 tests/gexp.scm | 20 ++++++++++++++++++++
 2 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/guix/gexp.scm b/guix/gexp.scm
index ad51bc55b78..012a6b2573a 100644
--- a/guix/gexp.scm
+++ b/guix/gexp.scm
@@ -728,19 +728,34 @@ (define-syntax-rule (with-parameters ((param value) ...) body ...)
                  (lambda ()
                    body ...)))
 
+(define (mcall-with-parameters parameters values thunk)
+  "Set PARAMETERS to VALUES for the dynamic extent of THUNK, a monadic
+procedure."
+  ;; This is the procedural variant of 'mparameterize'.
+  (define (set-value parameter value)
+    (parameter value))
+
+  ;; XXX: Non-local exits can leave PARAMETERS set to VALUES.
+  (mlet* %store-monad ((old-values
+                        (return (map set-value parameters values)))
+                       (result (thunk)))
+    (mbegin %store-monad
+      (return (map set-value parameters old-values)) ;restore old values
+      (return result))))
+
 (define-gexp-compiler compile-parameterized <parameterized>
   compiler =>
   (lambda (parameterized system target)
     (match (parameterized-bindings parameterized)
       (((parameters values) ...)
-       (let ((fluids (map parameter-fluid parameters))
-             (thunk  (parameterized-thunk parameterized)))
+       (let ((thunk (parameterized-thunk parameterized)))
          ;; Install the PARAMETERS for the dynamic extent of THUNK.
-         (with-fluids* fluids
-           (map (lambda (thunk) (thunk)) values)
+         ;; Special-case '%current-system' and '%current-target-system' to
+         ;; make sure we get the desired effect.
+         (mcall-with-parameters
+             parameters
+             (map (lambda (thunk) (thunk)) values)
            (lambda ()
-             ;; Special-case '%current-system' and '%current-target-system' to
-             ;; make sure we get the desired effect.
              (let ((system (if (memq %current-system parameters)
                                (%current-system)
                                system))
@@ -2350,4 +2365,6 @@ (define* (references-file item #:optional (name "references")
   (read-hash-extend #\$ read-ungexp)
   (read-hash-extend #\+ (cut read-ungexp <> <> #t)))
 
-;;; gexp.scm ends here
+;;; Local Variables:
+;;; eval: (put 'mcall-with-parameters 'scheme-indent-function 2)
+;;; End:
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 @@ (define %extension-package
     (return (string=? (derivation-file-name drv)
                       (derivation-file-name result)))))
 
+(test-assertm "with-parameters for %graft?"
+  (mlet* %store-monad ((replacement -> (package
+                                         (inherit %bootstrap-guile)
+                                         (name (string-upcase
+                                                (package-name
+                                                 %bootstrap-guile)))))
+                       (guile -> (package
+                                   (inherit %bootstrap-guile)
+                                   (replacement replacement)))
+                       (drv0   (package->derivation %bootstrap-guile))
+                       (drv1   (package->derivation replacement))
+                       (obj0 -> (with-parameters ((%graft? #f))
+                                  guile))
+                       (obj1 -> (with-parameters ((%graft? #t))
+                                  guile))
+                       (result0 (lower-object obj0))
+                       (result1 (lower-object obj1)))
+    (return (and (eq? drv0 result0)
+                 (eq? drv1 result1)))))
+
 (test-assert "with-parameters + file-append"
   (let* ((system (match (%current-system)
                    ("aarch64-linux" "x86_64-linux")

base-commit: 90aa90eb05429553402e0b5225d23f84742a9286
-- 
2.48.1





Added indication that bug 76485 blocks76488 Request was from Ludovic Courtès <ludo <at> gnu.org> to control <at> debbugs.gnu.org. (Mon, 24 Feb 2025 10:03:05 GMT) Full text and rfc822 format available.

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 25 Feb 2025 22:37:01 GMT) Full text and rfc822 format available.

Message #10 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: David Elsing <david.elsing <at> posteo.net>
To: Ludovic Courtès <ludo <at> gnu.org>, 76485 <at> debbugs.gnu.org
Subject: Re: [PATCH] gexp: ‘with-parameters’
 properly handles ‘%graft?’.
Date: Tue, 25 Feb 2025 22:36:31 +0000
[Message part 1 (text/plain, inline)]
Hello,

Ludovic Courtès <ludo <at> gnu.org> writes:

> +         (mcall-with-parameters
> +             parameters
> +             (map (lambda (thunk) (thunk)) values)

Composing the monadic value here is a good idea I think.

> +  ;; XXX: Non-local exits can leave PARAMETERS set to VALUES.
> +  (mlet* %store-monad ((old-values
> +                        (return (map set-value parameters values)))
> +                       (result (thunk)))
> +    (mbegin %store-monad
> +      (return (map set-value parameters old-values)) ;restore old values
> +      (return result))))

However, I'm not convinced it is meaningful to set the parameters for a
general monad, e.g. for the identity monad or the list monad, there is
no way for the parameters to have an effect, only during the evaluation
of a function. Instead, I would suggest to only do this for the state
monad, as the parameters have an effect during the function application
of the monadic value to the state. The same applies to `mparameterized'
in (guix monads). Do you think that makes sense?

This allows for the use of `with-fluids*', keeping the parameterization
local. In the following message is a patch with the changes, which
(apart from the issue below) appears to work.

When testing the patch however, I noticed that `with-fluids*' does not
work with prompts and I get the following error:

--8<---------------cut here---------------start------------->8---
ERROR: Wrong type (expecting resumable continuation): #<vm-continuation 7f595e325690>
--8<---------------cut here---------------end--------------->8---

Attached is a minimal example, which works fine if `with-fluids*' is
removed. Is this behavior expected (I didn't see it in the
documentation) or is it a bug in Guile?

Cheers,
David


[test-prompt-fluids.scm (text/plain, inline)]
(define p (make-parameter 1))

(define cont
  (call-with-prompt 'foo
	(lambda ()
	  (with-fluids* (list (parameter-fluid p))
		(list 2)
		(lambda ()
		  (+ (abort-to-prompt 'foo) (p)))))
	(lambda (k)
	  k)))

(p 3)

(pk (cont 0))

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 25 Feb 2025 22:50:02 GMT) Full text and rfc822 format available.

Message #13 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: David Elsing <david.elsing <at> posteo.net>
To: 76485 <at> debbugs.gnu.org
Cc: David Elsing <david.elsing <at> posteo.net>,
 Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH] [WIP] gexp: ‘with-parameters’ properly handles ‘%graft?’.
Date: Tue, 25 Feb 2025 22:48:58 +0000
Note: Using ‘with-fluids*’ with a prompt does not work in Guile:

<https://issues.guix.gnu.org/76485#2>

* .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-with-parameters.
* guix/monads.scm (mparameterize): Remove macro.
(state-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 <at> gnu.org>
---
 .dir-locals.el                 |  3 ++-
 etc/manifests/system-tests.scm |  2 +-
 etc/manifests/time-travel.scm  |  8 +++---
 gnu/tests.scm                  |  9 ++++---
 guix/gexp.scm                  | 42 ++++++++++++++++---------------
 guix/monads.scm                | 46 +++++++++++++++++++++-------------
 guix/store.scm                 |  2 ++
 tests/gexp.scm                 | 20 +++++++++++++++
 tests/monads.scm               | 20 +++++++--------
 9 files changed, 93 insertions(+), 59 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index d629b51c8a..76c9e12992 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -138,7 +138,8 @@
    (eval . (put 'munless 'scheme-indent-function 1))
    (eval . (put 'mlet* 'scheme-indent-function 2))
    (eval . (put 'mlet 'scheme-indent-function 2))
-   (eval . (put 'mparameterize 'scheme-indent-function 2))
+   (eval . (put 'state-parameterize 'scheme-indent-function 2))
+   (eval . (put 'store-parameterize 'scheme-indent-function 2))
    (eval . (put 'run-with-store 'scheme-indent-function 1))
    (eval . (put 'run-with-state 'scheme-indent-function 1))
    (eval . (put 'wrap-program 'scheme-indent-function 1))
diff --git a/etc/manifests/system-tests.scm b/etc/manifests/system-tests.scm
index 4e16c53dcf..430f507520 100644
--- a/etc/manifests/system-tests.scm
+++ b/etc/manifests/system-tests.scm
@@ -53,7 +53,7 @@ (define (tests-for-current-guix source commit)
     (map (lambda (test)
            (system-test
             (inherit test)
-            (value (mparameterize %store-monad ((current-guix-package guix))
+            (value (store-parameterize ((current-guix-package guix))
                      (system-test-value test)))))
          (match (getenv "TESTS")
            (#f
diff --git a/etc/manifests/time-travel.scm b/etc/manifests/time-travel.scm
index 039ca89889..5256d2195c 100644
--- a/etc/manifests/time-travel.scm
+++ b/etc/manifests/time-travel.scm
@@ -22,7 +22,7 @@
 (use-modules (srfi srfi-9) (ice-9 match)
              (guix channels) (guix gexp)
              ((guix store) #:select (%store-monad))
-             ((guix monads) #:select (mparameterize return))
+             ((guix monads) #:select (store-parameterize return))
              ((guix git) #:select (%repository-cache-directory))
              ((guix build utils) #:select (mkdir-p)))
 
@@ -40,9 +40,9 @@ (define-gexp-compiler (guix-instance-compiler (instance <guix-instance>)
      ;; When this manifest is evaluated by Cuirass, make sure it does not
      ;; fiddle with the cached checkout that Cuirass is also using since
      ;; concurrent accesses are unsafe.
-     (mparameterize %store-monad ((%repository-cache-directory
-                                   (string-append (%repository-cache-directory)
-                                                  "/time-travel/" system)))
+     (store-parameterize ((%repository-cache-directory
+                           (string-append (%repository-cache-directory)
+                                          "/time-travel/" system)))
        (return (mkdir-p (%repository-cache-directory)))
        (latest-channel-derivation channels)))))
 
diff --git a/gnu/tests.scm b/gnu/tests.scm
index 2a9e51511f..db9d1f0b0f 100644
--- a/gnu/tests.scm
+++ b/gnu/tests.scm
@@ -34,7 +34,7 @@ (define-module (gnu tests)
   #:use-module (gnu services shepherd)
   #:use-module (guix discovery)
   #:use-module (guix monads)
-  #:use-module ((guix store) #:select (%store-monad))
+  #:use-module ((guix store) #:select (%store-monad store-parameterize))
   #:use-module ((guix utils)
                 #:select (%current-system %current-target-system))
   #:use-module (srfi srfi-1)
@@ -289,9 +289,10 @@ (define (write-system-test test port)
 (define-gexp-compiler (compile-system-test (test <system-test>)
                                            system target)
   "Compile TEST to a derivation."
-  (mparameterize %store-monad ((%current-system system)
-                               (%current-target-system target))
-    (system-test-value test)))
+  (store-parameterize
+   ((%current-system system)
+    (%current-target-system target))
+   (system-test-value test)))
 
 (define (test-modules)
   "Return the list of modules that define system tests."
diff --git a/guix/gexp.scm b/guix/gexp.scm
index ad51bc55b7..9ce6810172 100644
--- a/guix/gexp.scm
+++ b/guix/gexp.scm
@@ -733,26 +733,28 @@ (define-gexp-compiler compile-parameterized <parameterized>
   (lambda (parameterized system target)
     (match (parameterized-bindings parameterized)
       (((parameters values) ...)
-       (let ((fluids (map parameter-fluid parameters))
-             (thunk  (parameterized-thunk parameterized)))
-         ;; Install the PARAMETERS for the dynamic extent of THUNK.
-         (with-fluids* fluids
-           (map (lambda (thunk) (thunk)) values)
-           (lambda ()
-             ;; Special-case '%current-system' and '%current-target-system' to
-             ;; make sure we get the desired effect.
-             (let ((system (if (memq %current-system parameters)
-                               (%current-system)
-                               system))
-                   (target (if (memq %current-target-system parameters)
-                               (%current-target-system)
-                               target)))
-               (match (thunk)
-                 ((? struct? obj)
-                  (lower-object obj system #:target target))
-                 (obj                             ;store item
-                  (with-monad %store-monad
-                    (return obj)))))))))))
+       (let ((thunk (parameterized-thunk parameterized))
+             (values (map (lambda (thunk) (thunk)) values)))
+         ;; Install the PARAMETERS for the store monad.
+         (state-with-parameters parameters values
+           ;; Install the PARAMETERS for the dynamic extent of THUNK.
+           ;; Special-case '%current-system' and '%current-target-system' to
+           ;; make sure we get the desired effect.
+           (with-fluids* (map parameter-fluid parameters)
+             values
+             (lambda ()
+               (let ((system (if (memq %current-system parameters)
+                                 (%current-system)
+                                 system))
+                     (target (if (memq %current-target-system parameters)
+                                 (%current-target-system)
+                                 target)))
+                 (match (thunk)
+                   ((? struct? obj)
+                    (lower-object obj system #:target target))
+                   (obj                             ;store item
+                    (with-monad %store-monad
+                      (return obj))))))))))))
 
   expander => (lambda (parameterized lowered output)
                 (match (parameterized-bindings parameterized)
diff --git a/guix/monads.scm b/guix/monads.scm
index 0bd8ac9315..431c2a6a25 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -40,7 +40,6 @@ (define-module (guix monads)
             mbegin
             mwhen
             munless
-            mparameterize
             lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
             listm
             foldm
@@ -58,7 +57,8 @@ (define-module (guix monads)
             set-current-state
             state-push
             state-pop
-            run-with-state))
+            run-with-state
+            state-parameterize))
 
 ;;; Commentary:
 ;;;
@@ -399,21 +399,6 @@ (define-syntax munless
          (mbegin %current-monad
            mexp0 mexp* ...)))))
 
-(define-syntax mparameterize
-  (syntax-rules ()
-    "This form implements dynamic scoping, similar to 'parameterize', but in a
-monadic context."
-    ((_ monad ((parameter value) rest ...) body ...)
-     (let ((old-value (parameter)))
-       (mbegin monad
-         ;; XXX: Non-local exits are not correctly handled.
-         (return (parameter value))
-         (mlet monad ((result (mparameterize monad (rest ...) body ...)))
-           (parameter old-value)
-           (return result)))))
-    ((_ monad () body ...)
-     (mbegin monad body ...))))
-
 (define-syntax define-lift
   (syntax-rules ()
     ((_ liftn (args ...))
@@ -600,4 +585,29 @@ (define (state-push value)
   (lambda (state)
     (values state (cons value state))))
 
-;;; monads.scm end here
+(define-public (state-with-parameters parameters values mval)
+  "Set PARAMETERS to VALUES for the dynamic extent of MVAL, a value in the
+state monad."
+  (lambda (state)
+    (with-fluids* (map parameter-fluid parameters)
+      values
+      (lambda ()
+        (run-with-state mval state)))))
+
+(define-syntax state-parameterize
+  (syntax-rules ()
+    "This form implements dynamic scoping, similar to 'parameterize', but also
+in the monadic context of the state monad."
+    ((_ ((param value) ...) body ...)
+     (let ((parameters (list param ...))
+           (values (list value ...)))
+       (state-with-parameters parameters values
+         ;; Install the parameters also for the evaluation of body ...
+         (with-fluids* (map parameter-fluid parameters)
+           values
+           (lambda ()
+             (mbegin %state-monad body ...))))))))
+
+;;; Local Variables:
+;;; eval: (put 'state-with-parameters 'scheme-indent-function 2)
+;;; End:
diff --git a/guix/store.scm b/guix/store.scm
index cf5848e580..bae8e7762b 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -178,6 +178,7 @@ (define-module (guix store)
             store-lift
             store-lower
             run-with-store
+            store-parameterize
             %guile-for-build
             current-system
             set-current-system
@@ -1919,6 +1920,7 @@ (define-syntax new (identifier-syntax old)))
 (define-alias %store-monad %state-monad)
 (define-alias store-return state-return)
 (define-alias store-bind state-bind)
+(define-alias store-parameterize state-parameterize)
 
 ;; Instantiate templates for %STORE-MONAD since it's syntactically different
 ;; from %STATE-MONAD.
diff --git a/tests/gexp.scm b/tests/gexp.scm
index e870f6cb1b..2376c70d1b 100644
--- a/tests/gexp.scm
+++ b/tests/gexp.scm
@@ -451,6 +451,26 @@ (define (match-input thing)
     (return (string=? (derivation-file-name drv)
                       (derivation-file-name result)))))
 
+(test-assertm "with-parameters for %graft?"
+  (mlet* %store-monad ((replacement -> (package
+                                         (inherit %bootstrap-guile)
+                                         (name (string-upcase
+                                                (package-name
+                                                 %bootstrap-guile)))))
+                       (guile -> (package
+                                   (inherit %bootstrap-guile)
+                                   (replacement replacement)))
+                       (drv0   (package->derivation %bootstrap-guile))
+                       (drv1   (package->derivation replacement))
+                       (obj0 -> (with-parameters ((%graft? #f))
+                                  guile))
+                       (obj1 -> (with-parameters ((%graft? #t))
+                                  guile))
+                       (result0 (lower-object obj0))
+                       (result1 (lower-object obj1)))
+    (return (and (eq? drv0 result0)
+                 (eq? drv1 result1)))))
+
 (test-assert "with-parameters + file-append"
   (let* ((system (match (%current-system)
                    ("aarch64-linux" "x86_64-linux")
diff --git a/tests/monads.scm b/tests/monads.scm
index 7f255f02bf..4c9adcc264 100644
--- a/tests/monads.scm
+++ b/tests/monads.scm
@@ -136,18 +136,16 @@ (define (g x)
          %monads
          %monad-run))
 
-(test-assert "mparameterize"
+(test-assert "state-parameterize"
   (let ((parameter (make-parameter 'outside)))
-    (every (lambda (monad run)
-             (equal?
-              (run (mlet monad ((outer (return (parameter)))
-                                (inner
-                                 (mparameterize monad ((parameter 'inside))
-                                   (return (parameter)))))
-                     (return (list outer inner (parameter)))))
-              '(outside inside outside)))
-           %monads
-           %monad-run)))
+    (equal?
+     (run-with-state
+         (mlet %state-monad ((outer (return (parameter)))
+                             (inner
+                              (state-parameterize ((parameter 'inside))
+                                                  (return (parameter)))))
+           (return (list outer inner (parameter)))))
+     '(outside inside outside))))
 
 (test-assert "mlet* + text-file + package-file"
   (run-with-store %store
-- 
2.48.1





Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Thu, 27 Feb 2025 09:40:01 GMT) Full text and rfc822 format available.

Message #16 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Thu, 27 Feb 2025 10:38:52 +0100
Hi David,

David Elsing <david.elsing <at> posteo.net> skribis:

>> +  ;; XXX: Non-local exits can leave PARAMETERS set to VALUES.
>> +  (mlet* %store-monad ((old-values
>> +                        (return (map set-value parameters values)))
>> +                       (result (thunk)))
>> +    (mbegin %store-monad
>> +      (return (map set-value parameters old-values)) ;restore old values
>> +      (return result))))
>
> However, I'm not convinced it is meaningful to set the parameters for a
> general monad, e.g. for the identity monad or the list monad, there is
> no way for the parameters to have an effect, only during the evaluation
> of a function.

Still, ‘mparameterized’ does the right thing, whether you’re using
‘%state-monad’ or ‘%identity-monad’:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user)> ,optimize (mparameterize %identity-monad ((current-output-port (%make-void-port "w")))
				 (mlet* %identity-monad ((x (return 2))
							 (y (return 3))
						         (z (return (+ x y))))
				   (return (* z 2))))
$26 = (let ((old-value (current-output-port)))
        (current-output-port (%make-void-port "w"))
        (let ((mvalue 10)) (current-output-port old-value) mvalue))
scheme@(guile-user)> ,optimize (mparameterize %state-monad ((current-output-port (%make-void-port "w")))
				 (mlet* %state-monad ((x (return 2))
						      (y (return 3))
						      (z (return (+ x y))))
				   (return (* z 2))))
$27 = (let* ((old-value (current-output-port))
             (mvalue
              (let ((value (current-output-port (%make-void-port "w"))))
                (lambda (state) (values value state)))))
        (lambda (state)
          (call-with-values
           (lambda () (mvalue state))
           (lambda (value state)
             ((begin
                (current-output-port old-value)
                (lambda (state) (values 10 state)))
              state)))))
--8<---------------cut here---------------end--------------->8---

That is, no matter what, it changes the value of the parameters before
evaluation of the body happens (whether or not it’s delayed) and changes
it back after.

> Instead, I would suggest to only do this for the state monad, as the
> parameters have an effect during the function application of the
> monadic value to the state. The same applies to `mparameterized' in
> (guix monads). Do you think that makes sense?

Yes, though it would be nice to have a variant of ‘parameterize’ “that
works for monad”, that was the intent of ‘mparameterize’.

Because of that, I have a preference for ‘mparameterize’ rather than
‘state-parameterize’ or any other specific variant.

WDYT?

> This allows for the use of `with-fluids*', keeping the parameterization
> local. In the following message is a patch with the changes, which
> (apart from the issue below) appears to work.

Yes, that’s because ‘with-fluids*’ is implemented in C, which makes it a
“continuation barrier” (continuations that contain C stack frames cannot
be resumed).  It’s a limitation of the current implementation rather
than a bug, strictly speaking.  :-)

Thanks,
Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Fri, 28 Feb 2025 08:30:03 GMT) Full text and rfc822 format available.

Message #19 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Fri, 28 Feb 2025 09:29:20 +0100
Ludovic Courtès <ludo <at> gnu.org> skribis:

> Yes, though it would be nice to have a variant of ‘parameterize’ “that
> works for monad”, that was the intent of ‘mparameterize’.

I meant: “that works for any monad”.

> Because of that, I have a preference for ‘mparameterize’ rather than
> ‘state-parameterize’ or any other specific variant.

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Sat, 01 Mar 2025 17:53:02 GMT) Full text and rfc822 format available.

Message #22 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: David Elsing <david.elsing <at> posteo.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Sat, 01 Mar 2025 17:52:31 +0000
[Message part 1 (text/plain, inline)]
Hi Ludo',

I thought about it some more and I think my issue is that in general,
the number of times a function passed to bind is evaluated is not fixed.
Attached is an example for a monad which acts as a combination of the
state monad and the maybe monad. I used two parameters, because they are
set at different times in the implementation of `mparameterize' (but
this seems to be a different issue to me). In particular, p2 is not
restored at the end, because no function application is done for a
"nothing" value.

Another problematic example would be a "coroutine" monad [1], which can
pause the evaluation of its "steps", during which the parameters could
be used or changed elsewhere.

Ludovic Courtès <ludo <at> gnu.org> writes:

> Yes, though it would be nice to have a variant of ‘parameterize’ “that
> works for any monad”, that was the intent of ‘mparameterize’.

I think what you describe (although I'm not too familiar with Haskell)
is like the ReaderT (or StateT if the parameters can change within the
monad) monad transformer, where the parameters are stored in the
additional state provided by ReaderT. This is however a new monad and
different from setting the parameters globally in different functions
passed to bind.

> Because of that, I have a preference for ‘mparameterize’ rather than
> ‘state-parameterize’ or any other specific variant.
>
> WDYT?

To my understanding, what we actually want is to affect the way the
function of the state monad applies to the passed state, or formulated
in a different way, the state to include the parameters. This would be
effectively achieved using `with-fluids*' inside the monadic procedure
(except that they are not part of the final state). It can be though of
expanding the state of the state monad with the parameters, where the
initial state contains the "outside" parameters. Of course, now there
are two different ways to pass on state, through the state monad and the
parameters... :)

Does this make sense?


Because `with-fluids*' does not work with prompts, I still think your
solution is a good workaround when specialized for the state monad, as
long as the parameters are not used externally until the execution is
completely finished.

> Yes, that’s because ‘with-fluids*’ is implemented in C, which makes it a
> “continuation barrier” (continuations that contain C stack frames cannot
> be resumed).  It’s a limitation of the current implementation rather
> than a bug, strictly speaking.  :-)

Ah I see, thanks for the explanation! Are there plans to change that and
do you think it would be difficult to do?

Maybe the suspension could also be done without prompts by instead
modifying the store monad, similar to [1]? It would probably be less
straightforward though and maybe better suitable for languages like
Haskell.

Cheers,
David

[1] https://hackage.haskell.org/package/monad-coroutine-0.9.2/docs/Control-Monad-Coroutine.html

[test-mparameterize.scm (text/plain, inline)]
(use-modules
 (guix monads)
 (ice-9 match))

(define-inlinable (test-return value)
  (lambda (state)
    (list 'just value state)))

(define-inlinable (test-bind mvalue mproc)
  (lambda (state)
    (match (mvalue state)
      (('just value state2)
       ((mproc value) state2))
      (('nothing state2)
       (list 'nothing state2)))))

(define-monad %test-monad
  (bind test-bind)
  (return test-return))

(define mval
  (mlet %test-monad
      ((a (return 3))
       (b (lambda (state) (list 'nothing state))))
    (return 2)))

(define p1 (make-parameter 1))
(define p2 (make-parameter 1))

(pk "A" (p1) (p2))

(define mval2
  (mparameterize %test-monad
      ((p1 3)
       (p2 3))
    mval))

(pk "B" (p1) (p2))

(p1 2)
(p2 2)

(pk "C" (p1) (p2))

(pk (mval2 1))

(pk "D" (p1) (p2))

;;; Output:

;;; ("A" 1 1)

;;; ("B" 3 1)

;;; ("C" 2 2)

;;; ((nothing 1))

;;; ("D" 2 3)

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Mon, 03 Mar 2025 18:04:01 GMT) Full text and rfc822 format available.

Message #25 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Mon, 03 Mar 2025 19:03:19 +0100
Hi David,

David Elsing <david.elsing <at> posteo.net> skribis:

> I thought about it some more and I think my issue is that in general,
> the number of times a function passed to bind is evaluated is not fixed.
> Attached is an example for a monad which acts as a combination of the
> state monad and the maybe monad. I used two parameters, because they are
> set at different times in the implementation of `mparameterize' (but
> this seems to be a different issue to me). In particular, p2 is not
> restored at the end, because no function application is done for a
> "nothing" value.
>
> Another problematic example would be a "coroutine" monad [1], which can
> pause the evaluation of its "steps", during which the parameters could
> be used or changed elsewhere.

Hmm right.

I guess my contradiction is that I’m looking for a “generic” solution
but whose genericity is limited by the set of monads defined in (guix
monads) and by my imagination.  :-)

>> Because of that, I have a preference for ‘mparameterize’ rather than
>> ‘state-parameterize’ or any other specific variant.
>>
>> WDYT?
>
> To my understanding, what we actually want is to affect the way the
> function of the state monad applies to the passed state, or formulated
> in a different way, the state to include the parameters. This would be
> effectively achieved using `with-fluids*' inside the monadic procedure
> (except that they are not part of the final state). It can be though of
> expanding the state of the state monad with the parameters, where the
> initial state contains the "outside" parameters. Of course, now there
> are two different ways to pass on state, through the state monad and the
> parameters... :)
>
> Does this make sense?

I think so. :-)

The core of the problem here is that (guix monads), to a large extent,
addresses problems already addressed by other Scheme constructs such as
parameters/fluids, but in an incompatible way.  So really,
‘mparameterize’ and the many commits that fixed interactions between
‘%current-system’ & co. and the monad are really band aid.

So in this case I’m also looking for a “quick fix” more than extending
(guix monads).

> Because `with-fluids*' does not work with prompts, I still think your
> solution is a good workaround when specialized for the state monad, as
> long as the parameters are not used externally until the execution is
> completely finished.

Yes, the semantics are clumsy.

Would you like to send a patch that does it the way you want?
Preferably limited to fixing ‘with-parameters’ in particular so it also
works for ‘%graft?’.

Or are you saying that the patch at the beginning of this thread (where
‘mcall-with-parameters’ is specialized for the state monad) is good
enough?

Tell me what you prefer.  :-)

(There’s a couple of patch series depending on this fix.)

Thanks,
Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 20:32:02 GMT) Full text and rfc822 format available.

Message #28 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: David Elsing <david.elsing <at> posteo.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 20:31:39 +0000
Hi Ludo',

Ludovic Courtès <ludo <at> gnu.org> writes:

> I guess my contradiction is that I’m looking for a “generic” solution
> but whose genericity is limited by the set of monads defined in (guix
> monads) and by my imagination.  :-)

Ah yes, but I don't think a generic solution is needed at this point, as
only the state monad is used in Guix. :)
I also tried to find a general way to apply `with-fluids*' for monads in
`mparameterize' and then noticed it might not make much sense.

> The core of the problem here is that (guix monads), to a large extent,
> addresses problems already addressed by other Scheme constructs such as
> parameters/fluids, but in an incompatible way.  So really,
> ‘mparameterize’ and the many commits that fixed interactions between
> ‘%current-system’ & co. and the monad are really band aid.

I don't find the combination of the state monad and parameters so bad,
and the idea of using the store monad to accumulate what to build is
quite nice. To me the combination with prompts is a bit confusing
though, it might be clearer to have continuations as part of the store
monad.

> So in this case I’m also looking for a “quick fix” more than extending
> (guix monads).

Yes that makes sense, in my opinion a quick fix for the state monad is
fine.

> Would you like to send a patch that does it the way you want?
> Preferably limited to fixing ‘with-parameters’ in particular so it also
> works for ‘%graft?’.
>
> Or are you saying that the patch at the beginning of this thread (where
> ‘mcall-with-parameters’ is specialized for the state monad) is good
> enough?

Oh, maybe you didn't see my previous patch where I specialized
`mcall-with-parameters' and `mparameterize' to the state monad, I only
sent it to Debbugs. I updated the patch and used your workaround for
avoiding using `with-fluids*' by setting and later restoring the
parameters.

Cheers,
David




Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 20:37:02 GMT) Full text and rfc822 format available.

Message #31 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: David Elsing <david.elsing <at> posteo.net>
To: 76485 <at> debbugs.gnu.org
Cc: David Elsing <david.elsing <at> posteo.net>, ludo <at> gnu.org
Subject: [PATCH v2] gexp: ‘with-parameters’ properly handles ‘%graft?’.
Date: Tue,  4 Mar 2025 20:33:08 +0000
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 <at> gnu.org>
---
 .dir-locals.el                 |  3 +-
 etc/manifests/system-tests.scm |  2 +-
 etc/manifests/time-travel.scm  |  8 +++---
 gnu/tests.scm                  |  8 +++---
 guix/gexp.scm                  | 42 ++++++++++++++-------------
 guix/monads.scm                | 52 +++++++++++++++++++++++-----------
 guix/store.scm                 |  2 ++
 tests/gexp.scm                 | 20 +++++++++++++
 tests/monads.scm               | 20 ++++++-------
 9 files changed, 99 insertions(+), 58 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index d629b51c8a..76c9e12992 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -138,7 +138,8 @@
    (eval . (put 'munless 'scheme-indent-function 1))
    (eval . (put 'mlet* 'scheme-indent-function 2))
    (eval . (put 'mlet 'scheme-indent-function 2))
-   (eval . (put 'mparameterize 'scheme-indent-function 2))
+   (eval . (put 'state-parameterize 'scheme-indent-function 2))
+   (eval . (put 'store-parameterize 'scheme-indent-function 2))
    (eval . (put 'run-with-store 'scheme-indent-function 1))
    (eval . (put 'run-with-state 'scheme-indent-function 1))
    (eval . (put 'wrap-program 'scheme-indent-function 1))
diff --git a/etc/manifests/system-tests.scm b/etc/manifests/system-tests.scm
index 4e16c53dcf..430f507520 100644
--- a/etc/manifests/system-tests.scm
+++ b/etc/manifests/system-tests.scm
@@ -53,7 +53,7 @@ (define (tests-for-current-guix source commit)
     (map (lambda (test)
            (system-test
             (inherit test)
-            (value (mparameterize %store-monad ((current-guix-package guix))
+            (value (store-parameterize ((current-guix-package guix))
                      (system-test-value test)))))
          (match (getenv "TESTS")
            (#f
diff --git a/etc/manifests/time-travel.scm b/etc/manifests/time-travel.scm
index 039ca89889..5256d2195c 100644
--- a/etc/manifests/time-travel.scm
+++ b/etc/manifests/time-travel.scm
@@ -22,7 +22,7 @@
 (use-modules (srfi srfi-9) (ice-9 match)
              (guix channels) (guix gexp)
              ((guix store) #:select (%store-monad))
-             ((guix monads) #:select (mparameterize return))
+             ((guix monads) #:select (store-parameterize return))
              ((guix git) #:select (%repository-cache-directory))
              ((guix build utils) #:select (mkdir-p)))
 
@@ -40,9 +40,9 @@ (define-gexp-compiler (guix-instance-compiler (instance <guix-instance>)
      ;; When this manifest is evaluated by Cuirass, make sure it does not
      ;; fiddle with the cached checkout that Cuirass is also using since
      ;; concurrent accesses are unsafe.
-     (mparameterize %store-monad ((%repository-cache-directory
-                                   (string-append (%repository-cache-directory)
-                                                  "/time-travel/" system)))
+     (store-parameterize ((%repository-cache-directory
+                           (string-append (%repository-cache-directory)
+                                          "/time-travel/" system)))
        (return (mkdir-p (%repository-cache-directory)))
        (latest-channel-derivation channels)))))
 
diff --git a/gnu/tests.scm b/gnu/tests.scm
index 2a9e51511f..1e3dbf0944 100644
--- a/gnu/tests.scm
+++ b/gnu/tests.scm
@@ -34,7 +34,7 @@ (define-module (gnu tests)
   #:use-module (gnu services shepherd)
   #:use-module (guix discovery)
   #:use-module (guix monads)
-  #:use-module ((guix store) #:select (%store-monad))
+  #:use-module ((guix store) #:select (%store-monad store-parameterize))
   #:use-module ((guix utils)
                 #:select (%current-system %current-target-system))
   #:use-module (srfi srfi-1)
@@ -289,9 +289,9 @@ (define (write-system-test test port)
 (define-gexp-compiler (compile-system-test (test <system-test>)
                                            system target)
   "Compile TEST to a derivation."
-  (mparameterize %store-monad ((%current-system system)
-                               (%current-target-system target))
-    (system-test-value test)))
+  (store-parameterize ((%current-system system)
+                       (%current-target-system target))
+      (system-test-value test)))
 
 (define (test-modules)
   "Return the list of modules that define system tests."
diff --git a/guix/gexp.scm b/guix/gexp.scm
index ad51bc55b7..9ce6810172 100644
--- a/guix/gexp.scm
+++ b/guix/gexp.scm
@@ -733,26 +733,28 @@ (define-gexp-compiler compile-parameterized <parameterized>
   (lambda (parameterized system target)
     (match (parameterized-bindings parameterized)
       (((parameters values) ...)
-       (let ((fluids (map parameter-fluid parameters))
-             (thunk  (parameterized-thunk parameterized)))
-         ;; Install the PARAMETERS for the dynamic extent of THUNK.
-         (with-fluids* fluids
-           (map (lambda (thunk) (thunk)) values)
-           (lambda ()
-             ;; Special-case '%current-system' and '%current-target-system' to
-             ;; make sure we get the desired effect.
-             (let ((system (if (memq %current-system parameters)
-                               (%current-system)
-                               system))
-                   (target (if (memq %current-target-system parameters)
-                               (%current-target-system)
-                               target)))
-               (match (thunk)
-                 ((? struct? obj)
-                  (lower-object obj system #:target target))
-                 (obj                             ;store item
-                  (with-monad %store-monad
-                    (return obj)))))))))))
+       (let ((thunk (parameterized-thunk parameterized))
+             (values (map (lambda (thunk) (thunk)) values)))
+         ;; Install the PARAMETERS for the store monad.
+         (state-with-parameters parameters values
+           ;; Install the PARAMETERS for the dynamic extent of THUNK.
+           ;; Special-case '%current-system' and '%current-target-system' to
+           ;; make sure we get the desired effect.
+           (with-fluids* (map parameter-fluid parameters)
+             values
+             (lambda ()
+               (let ((system (if (memq %current-system parameters)
+                                 (%current-system)
+                                 system))
+                     (target (if (memq %current-target-system parameters)
+                                 (%current-target-system)
+                                 target)))
+                 (match (thunk)
+                   ((? struct? obj)
+                    (lower-object obj system #:target target))
+                   (obj                             ;store item
+                    (with-monad %store-monad
+                      (return obj))))))))))))
 
   expander => (lambda (parameterized lowered output)
                 (match (parameterized-bindings parameterized)
diff --git a/guix/monads.scm b/guix/monads.scm
index 0bd8ac9315..0df82bb465 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -40,7 +40,6 @@ (define-module (guix monads)
             mbegin
             mwhen
             munless
-            mparameterize
             lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
             listm
             foldm
@@ -58,7 +57,8 @@ (define-module (guix monads)
             set-current-state
             state-push
             state-pop
-            run-with-state))
+            run-with-state
+            state-parameterize))
 
 ;;; Commentary:
 ;;;
@@ -399,21 +399,6 @@ (define-syntax munless
          (mbegin %current-monad
            mexp0 mexp* ...)))))
 
-(define-syntax mparameterize
-  (syntax-rules ()
-    "This form implements dynamic scoping, similar to 'parameterize', but in a
-monadic context."
-    ((_ monad ((parameter value) rest ...) body ...)
-     (let ((old-value (parameter)))
-       (mbegin monad
-         ;; XXX: Non-local exits are not correctly handled.
-         (return (parameter value))
-         (mlet monad ((result (mparameterize monad (rest ...) body ...)))
-           (parameter old-value)
-           (return result)))))
-    ((_ monad () body ...)
-     (mbegin monad body ...))))
-
 (define-syntax define-lift
   (syntax-rules ()
     ((_ liftn (args ...))
@@ -600,4 +585,37 @@ (define (state-push value)
   (lambda (state)
     (values state (cons value state))))
 
+(define-public (state-with-parameters parameters parameter-values mval)
+  "Set PARAMETERS to PARAMETER-VALUES for the dynamic extent of MVAL, a value
+in the state monad."
+  (define (set-value parameter value)
+    (parameter value))
+
+  (lambda (state)
+    ;; XXX: 'with-fluids*' does not work with prompts, therefore the parameters
+    ;; are set globally. This leaves the parameters changed upon a non-local
+    ;; exit and restores them only after running MVAL to completion. See
+    ;; <https://issues.guix.gnu.org/76485>.
+    (let ((old-values (map set-value parameters parameter-values)))
+      (call-with-values
+          (lambda ()
+            (mval state))
+        (lambda (value state)
+          (map set-value parameters old-values)
+          (values value state))))))
+
+(define-syntax state-parameterize
+  (syntax-rules ()
+    "This form implements dynamic scoping, similar to 'parameterize', but also
+in the monadic context of the state monad."
+    ((_ ((param value) ...) body ...)
+     (let ((parameters (list param ...))
+           (values (list value ...)))
+       (state-with-parameters parameters values
+         ;; Install the parameters also for the evaluation of body ...
+         (with-fluids* (map parameter-fluid parameters)
+           values
+           (lambda ()
+             (mbegin %state-monad body ...))))))))
+
 ;;; monads.scm end here
diff --git a/guix/store.scm b/guix/store.scm
index cf5848e580..bae8e7762b 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -178,6 +178,7 @@ (define-module (guix store)
             store-lift
             store-lower
             run-with-store
+            store-parameterize
             %guile-for-build
             current-system
             set-current-system
@@ -1919,6 +1920,7 @@ (define-syntax new (identifier-syntax old)))
 (define-alias %store-monad %state-monad)
 (define-alias store-return state-return)
 (define-alias store-bind state-bind)
+(define-alias store-parameterize state-parameterize)
 
 ;; Instantiate templates for %STORE-MONAD since it's syntactically different
 ;; from %STATE-MONAD.
diff --git a/tests/gexp.scm b/tests/gexp.scm
index e870f6cb1b..2376c70d1b 100644
--- a/tests/gexp.scm
+++ b/tests/gexp.scm
@@ -451,6 +451,26 @@ (define (match-input thing)
     (return (string=? (derivation-file-name drv)
                       (derivation-file-name result)))))
 
+(test-assertm "with-parameters for %graft?"
+  (mlet* %store-monad ((replacement -> (package
+                                         (inherit %bootstrap-guile)
+                                         (name (string-upcase
+                                                (package-name
+                                                 %bootstrap-guile)))))
+                       (guile -> (package
+                                   (inherit %bootstrap-guile)
+                                   (replacement replacement)))
+                       (drv0   (package->derivation %bootstrap-guile))
+                       (drv1   (package->derivation replacement))
+                       (obj0 -> (with-parameters ((%graft? #f))
+                                  guile))
+                       (obj1 -> (with-parameters ((%graft? #t))
+                                  guile))
+                       (result0 (lower-object obj0))
+                       (result1 (lower-object obj1)))
+    (return (and (eq? drv0 result0)
+                 (eq? drv1 result1)))))
+
 (test-assert "with-parameters + file-append"
   (let* ((system (match (%current-system)
                    ("aarch64-linux" "x86_64-linux")
diff --git a/tests/monads.scm b/tests/monads.scm
index 7f255f02bf..c05d13776a 100644
--- a/tests/monads.scm
+++ b/tests/monads.scm
@@ -136,18 +136,16 @@ (define (g x)
          %monads
          %monad-run))
 
-(test-assert "mparameterize"
+(test-assert "state-parameterize"
   (let ((parameter (make-parameter 'outside)))
-    (every (lambda (monad run)
-             (equal?
-              (run (mlet monad ((outer (return (parameter)))
-                                (inner
-                                 (mparameterize monad ((parameter 'inside))
-                                   (return (parameter)))))
-                     (return (list outer inner (parameter)))))
-              '(outside inside outside)))
-           %monads
-           %monad-run)))
+    (equal?
+     (run-with-state
+         (mlet %state-monad ((outer (return (parameter)))
+                             (inner
+                              (state-parameterize ((parameter 'inside))
+                                  (return (parameter)))))
+           (return (list outer inner (parameter)))))
+     '(outside inside outside))))
 
 (test-assert "mlet* + text-file + package-file"
   (run-with-store %store
-- 
2.48.1





Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:16:02 GMT) Full text and rfc822 format available.

Message #34 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH v2] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:13:38 +0100
[Message part 1 (text/plain, inline)]
Hello,

David Elsing <david.elsing <at> posteo.net> skribis:

> 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 <at> gnu.org>

Applied with the change below, in accordance with the deprecation
policy.

Thank you!

Ludo’.

PS: Let me know if I got the copyright line wrong.

[Message part 2 (text/x-patch, inline)]
diff --git a/guix/monads.scm b/guix/monads.scm
index 0df82bb4653..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing <at> posteo.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -19,6 +20,7 @@
 (define-module (guix monads)
   #:use-module ((system syntax)
                 #:select (syntax-local-binding))
+  #:autoload   (guix deprecation) (warn-about-deprecation)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -58,7 +60,8 @@ (define-module (guix monads)
             state-push
             state-pop
             run-with-state
-            state-parameterize))
+            state-parameterize
+            mparameterize))
 
 ;;; Commentary:
 ;;;
@@ -618,4 +621,15 @@ (define-syntax state-parameterize
            (lambda ()
              (mbegin %state-monad body ...))))))))
 
+(define-syntax mparameterize                  ;can be removed after 2026-03-05
+  (lambda (s)
+    "This is the old form for 'state-parameterize', which pretended to work
+with any monad but was in fact specialized for '%state-monad'."
+    (syntax-case s ()
+      ((_ monad bindings body ...)
+       (begin
+         (warn-about-deprecation 'mparameterize (current-source-location)
+                                 #:replacement 'state-parameterize)
+         #'(state-parameterize bindings body ...))))))
+
 ;;; monads.scm end here

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:17:01 GMT) Full text and rfc822 format available.

Message #37 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH v2] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:09:09 +0100
[Message part 1 (text/plain, inline)]
Hello,

David Elsing <david.elsing <at> posteo.net> skribis:

> 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 <at> gnu.org>

Applied with the change below, in accordance with the deprecation
policy.

Thank you!

Ludo’.

PS: Let me know if I got the copyright line wrong.

[Message part 2 (text/x-patch, inline)]
diff --git a/guix/monads.scm b/guix/monads.scm
index 0df82bb4653..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing <at> posteo.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -19,6 +20,7 @@
 (define-module (guix monads)
   #:use-module ((system syntax)
                 #:select (syntax-local-binding))
+  #:autoload   (guix deprecation) (warn-about-deprecation)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -58,7 +60,8 @@ (define-module (guix monads)
             state-push
             state-pop
             run-with-state
-            state-parameterize))
+            state-parameterize
+            mparameterize))
 
 ;;; Commentary:
 ;;;
@@ -618,4 +621,15 @@ (define-syntax state-parameterize
            (lambda ()
              (mbegin %state-monad body ...))))))))
 
+(define-syntax mparameterize                  ;can be removed after 2026-03-05
+  (lambda (s)
+    "This is the old form for 'state-parameterize', which pretended to work
+with any monad but was in fact specialized for '%state-monad'."
+    (syntax-case s ()
+      ((_ monad bindings body ...)
+       (begin
+         (warn-about-deprecation 'mparameterize (current-source-location)
+                                 #:replacement 'state-parameterize)
+         #'(state-parameterize bindings body ...))))))
+
 ;;; monads.scm end here

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:17:02 GMT) Full text and rfc822 format available.

Message #40 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH v2] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:10:28 +0100
[Message part 1 (text/plain, inline)]
Hello,

David Elsing <david.elsing <at> posteo.net> skribis:

> 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 <at> gnu.org>

Applied with the change below, in accordance with the deprecation
policy.

Thank you!

Ludo’.

PS: Let me know if I got the copyright line wrong.

[Message part 2 (text/x-patch, inline)]
diff --git a/guix/monads.scm b/guix/monads.scm
index 0df82bb4653..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing <at> posteo.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -19,6 +20,7 @@
 (define-module (guix monads)
   #:use-module ((system syntax)
                 #:select (syntax-local-binding))
+  #:autoload   (guix deprecation) (warn-about-deprecation)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -58,7 +60,8 @@ (define-module (guix monads)
             state-push
             state-pop
             run-with-state
-            state-parameterize))
+            state-parameterize
+            mparameterize))
 
 ;;; Commentary:
 ;;;
@@ -618,4 +621,15 @@ (define-syntax state-parameterize
            (lambda ()
              (mbegin %state-monad body ...))))))))
 
+(define-syntax mparameterize                  ;can be removed after 2026-03-05
+  (lambda (s)
+    "This is the old form for 'state-parameterize', which pretended to work
+with any monad but was in fact specialized for '%state-monad'."
+    (syntax-case s ()
+      ((_ monad bindings body ...)
+       (begin
+         (warn-about-deprecation 'mparameterize (current-source-location)
+                                 #:replacement 'state-parameterize)
+         #'(state-parameterize bindings body ...))))))
+
 ;;; monads.scm end here

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:17:02 GMT) Full text and rfc822 format available.

Message #43 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH v2] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:09:59 +0100
[Message part 1 (text/plain, inline)]
Hello,

David Elsing <david.elsing <at> posteo.net> skribis:

> 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 <at> gnu.org>

Applied with the change below, in accordance with the deprecation
policy.

Thank you!

Ludo’.

PS: Let me know if I got the copyright line wrong.

[Message part 2 (text/x-patch, inline)]
diff --git a/guix/monads.scm b/guix/monads.scm
index 0df82bb4653..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing <at> posteo.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -19,6 +20,7 @@
 (define-module (guix monads)
   #:use-module ((system syntax)
                 #:select (syntax-local-binding))
+  #:autoload   (guix deprecation) (warn-about-deprecation)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -58,7 +60,8 @@ (define-module (guix monads)
             state-push
             state-pop
             run-with-state
-            state-parameterize))
+            state-parameterize
+            mparameterize))
 
 ;;; Commentary:
 ;;;
@@ -618,4 +621,15 @@ (define-syntax state-parameterize
            (lambda ()
              (mbegin %state-monad body ...))))))))
 
+(define-syntax mparameterize                  ;can be removed after 2026-03-05
+  (lambda (s)
+    "This is the old form for 'state-parameterize', which pretended to work
+with any monad but was in fact specialized for '%state-monad'."
+    (syntax-case s ()
+      ((_ monad bindings body ...)
+       (begin
+         (warn-about-deprecation 'mparameterize (current-source-location)
+                                 #:replacement 'state-parameterize)
+         #'(state-parameterize bindings body ...))))))
+
 ;;; monads.scm end here

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:17:03 GMT) Full text and rfc822 format available.

Message #46 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH v2] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:08:11 +0100
[Message part 1 (text/plain, inline)]
Hello,

David Elsing <david.elsing <at> posteo.net> skribis:

> 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 <at> gnu.org>

Applied with the change below, in accordance with the deprecation
policy.

Thank you!

Ludo’.

PS: Let me know if I got the copyright line wrong.

[Message part 2 (text/x-patch, inline)]
diff --git a/guix/monads.scm b/guix/monads.scm
index 0df82bb4653..e1b056dc95f 100644
--- a/guix/monads.scm
+++ b/guix/monads.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing <at> posteo.net>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -19,6 +20,7 @@
 (define-module (guix monads)
   #:use-module ((system syntax)
                 #:select (syntax-local-binding))
+  #:autoload   (guix deprecation) (warn-about-deprecation)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -58,7 +60,8 @@ (define-module (guix monads)
             state-push
             state-pop
             run-with-state
-            state-parameterize))
+            state-parameterize
+            mparameterize))
 
 ;;; Commentary:
 ;;;
@@ -618,4 +621,15 @@ (define-syntax state-parameterize
            (lambda ()
              (mbegin %state-monad body ...))))))))
 
+(define-syntax mparameterize                  ;can be removed after 2026-03-05
+  (lambda (s)
+    "This is the old form for 'state-parameterize', which pretended to work
+with any monad but was in fact specialized for '%state-monad'."
+    (syntax-case s ()
+      ((_ monad bindings body ...)
+       (begin
+         (warn-about-deprecation 'mparameterize (current-source-location)
+                                 #:replacement 'state-parameterize)
+         #'(state-parameterize bindings body ...))))))
+
 ;;; monads.scm end here

Information forwarded to guix-patches <at> gnu.org:
bug#76485; Package guix-patches. (Tue, 04 Mar 2025 22:19:02 GMT) Full text and rfc822 format available.

Message #49 received at 76485 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: David Elsing <david.elsing <at> posteo.net>
Cc: 76485 <at> debbugs.gnu.org
Subject: Re: [bug#76485] [PATCH] gexp: ‘with-parameters’ properly handles
 ‘%graft?’.
Date: Tue, 04 Mar 2025 23:18:25 +0100
Hey David,

David Elsing <david.elsing <at> posteo.net> skribis:

> Ah yes, but I don't think a generic solution is needed at this point, as
> only the state monad is used in Guix. :)
> I also tried to find a general way to apply `with-fluids*' for monads in
> `mparameterize' and then noticed it might not make much sense.

Yeah, I agree.

>> The core of the problem here is that (guix monads), to a large extent,
>> addresses problems already addressed by other Scheme constructs such as
>> parameters/fluids, but in an incompatible way.  So really,
>> ‘mparameterize’ and the many commits that fixed interactions between
>> ‘%current-system’ & co. and the monad are really band aid.
>
> I don't find the combination of the state monad and parameters so bad,
> and the idea of using the store monad to accumulate what to build is
> quite nice.

That’s right, glad you like it.  :-)

Overall I think the initial motivation for having monads (the “store”
monad in particular) still makes sense.  It’s the integration that
turned out to be clumsier than expected.

> To me the combination with prompts is a bit confusing though, it might
> be clearer to have continuations as part of the store monad.

I’m not sure.  In a way, file-like objects achieve something similar to
the store monad, but in a more “Schemey” way.  Likewise, Scheme has
delimited continuations, which is dynamic in nature; in other languages
one would use a monad for that but in Scheme it would be questionable as
it wouldn’t play well with other features (first of all by
“contaminating” non-monadic procedures).

> Oh, maybe you didn't see my previous patch where I specialized
> `mcall-with-parameters' and `mparameterize' to the state monad, I only
> sent it to Debbugs. I updated the patch and used your workaround for
> avoiding using `with-fluids*' by setting and later restoring the
> parameters.

I actually did see it but I was in a state of confusion.  :-)

Thanks for your patience and for your work!

Ludo’.




bug closed, send any further explanations to 76485 <at> debbugs.gnu.org and Ludovic Courtès <ludo <at> gnu.org> Request was from Ludovic Courtès <ludo <at> gnu.org> to control <at> debbugs.gnu.org. (Tue, 04 Mar 2025 22:19:02 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 02 Apr 2025 11:24:31 GMT) Full text and rfc822 format available.

This bug report was last modified 79 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.