[PATCH] gexp: ‘with-parameters’ properly handles ‘%graft?’.

  • Done
  • quality assurance status badge
Details
2 participants
  • David Elsing
  • Ludovic Courtès
Owner
unassigned
Submitted by
Ludovic Courtès
Severity
normal

Debbugs page

Ludovic Courtès wrote 2 weeks ago
[PATCH] gexp: ‘with-parameters’ pro perly handles ‘%graft?’.
(address . guix-patches@gnu.org)
256e623843a70b001801dcddd7acb4138e6216b4.1740236293.git.ludo@gnu.org

* 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@posteo.net>
Change-Id: Iddda7ead2aeef24dd989ac37a53fc99b726731b3
---
guix/gexp.scm | 31 ++++++++++++++++++++++++-------
tests/gexp.scm | 20 ++++++++++++++++++++
2 files changed, 44 insertions(+), 7 deletions(-)

Toggle diff (88 lines)
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
Ludovic Courtès wrote 2 weeks ago
control message for bug #76488
(address . control@debbugs.gnu.org)
87jz9fslsj.fsf@gnu.org
block 76488 by 76485
quit
David Elsing wrote 2 weeks ago
Re: [PATCH] gexp: ‘with-parameters’ properly handles ‘%graft?’.
86jz9dslcg.fsf@posteo.net
Hello,

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

Toggle quote (4 lines)
> + (mcall-with-parameters
> + parameters
> + (map (lambda (thunk) (thunk)) values)

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

Toggle quote (8 lines)
> + ;; 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:

Toggle snippet (3 lines)
ERROR: Wrong type (expecting resumable continuation): #<vm-continuation 7f595e325690>

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
(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))
David Elsing wrote 2 weeks ago
[PATCH] [WIP] gexp: ‘with-parameters ’ properly handles ‘%graft?’.
(address . 76485@debbugs.gnu.org)
20250225224907.30550-1-david.elsing@posteo.net
Note: Using ‘with-fluids*’ with a prompt does not work in Guile:


* .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@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(-)

Toggle diff (293 lines)
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
Ludovic Courtès wrote 2 weeks ago
Re: [bug#76485] [PATCH] gexp: ‘with-parameters ’ properly handles ‘%graft?’.
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87ldtrybf7.fsf@gnu.org
Hi David,

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

Toggle quote (13 lines)
>> + ;; 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’:

Toggle snippet (27 lines)
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)))))

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.

Toggle quote (5 lines)
> 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?

Toggle quote (4 lines)
> 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’.
Ludovic Courtès wrote 1 weeks ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87senyv5en.fsf@gnu.org
Ludovic Courtès <ludo@gnu.org> skribis:

Toggle quote (3 lines)
> 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”.

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

Ludo’.
David Elsing wrote 1 weeks ago
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 76485@debbugs.gnu.org)
86eczgsko0.fsf@posteo.net
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@gnu.org> writes:

Toggle quote (3 lines)
> 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.

Toggle quote (5 lines)
> 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.

Toggle quote (5 lines)
> 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

(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)
Ludovic Courtès wrote 6 days ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87zfi2c7q0.fsf@gnu.org
Hi David,

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

Toggle quote (13 lines)
> 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. :-)

Toggle quote (17 lines)
>> 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).

Toggle quote (5 lines)
> 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’.
David Elsing wrote 5 days ago
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 76485@debbugs.gnu.org)
868qpky1uc.fsf@posteo.net
Hi Ludo',

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

Toggle quote (4 lines)
> 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.

Toggle quote (6 lines)
> 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.

Toggle quote (3 lines)
> 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.

Toggle quote (8 lines)
> 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
David Elsing wrote 5 days ago
[PATCH v2] gexp: ‘with-parameters’ properly handles ‘%graft?’.
(address . 76485@debbugs.gnu.org)
20250304203337.2628-1-david.elsing@posteo.net

* .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>
---
.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(-)

Toggle diff (299 lines)
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
Ludovic Courtès wrote 5 days ago
Re: [bug#76485] [PATCH v2] gexp: ‘with-paramete rs’ properly handles ‘%graft?’.
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87sens8mwd.fsf@gnu.org
Hello,

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

Toggle quote (19 lines)
>
> * .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>

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.
Toggle diff (46 lines)
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@gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing@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
Ludovic Courtès wrote 5 days ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87y0xk8n3u.fsf@gnu.org
Hello,

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

Toggle quote (19 lines)
>
> * .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>

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.
Toggle diff (46 lines)
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@gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing@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
Ludovic Courtès wrote 5 days ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87v7so8n1n.fsf@gnu.org
Hello,

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

Toggle quote (19 lines)
>
> * .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>

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.
Toggle diff (46 lines)
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@gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing@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
Ludovic Courtès wrote 5 days ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87wmd48n2g.fsf@gnu.org
Hello,

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

Toggle quote (19 lines)
>
> * .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>

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.
Toggle diff (46 lines)
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@gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing@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
Ludovic Courtès wrote 5 days ago
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87zfi08n5g.fsf@gnu.org
Hello,

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

Toggle quote (19 lines)
>
> * .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>

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.
Toggle diff (46 lines)
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@gnu.org>
+;;; Copyright © 2013-2015, 2017, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 David Elsing <david.elsing@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
Ludovic Courtès wrote 5 days ago
Re: [bug#76485] [PATCH] gexp: ‘with-parameters ’ properly handles ‘%graft?’.
(name . David Elsing)(address . david.elsing@posteo.net)(address . 76485@debbugs.gnu.org)
87ikoo8moe.fsf@gnu.org
Hey David,

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

Toggle quote (5 lines)
> 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.

Toggle quote (10 lines)
>> 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.

Toggle quote (3 lines)
> 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).

Toggle quote (6 lines)
> 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’.
Ludovic Courtès wrote 5 days ago
control message for bug #76485
(address . control@debbugs.gnu.org)
87h6488mnz.fsf@gnu.org
close 76485
quit
?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send an email to 76485@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 76485
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch
You may also tag this issue. See list of standard tags. For example, to set the confirmed and easy tags
mumi command -t +confirmed -t +easy
Or, remove the moreinfo tag and set the help tag
mumi command -t -moreinfo -t +help