mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-24 05:18:07 -05:00
monads: Allow n-ary '>>=' expressions.
Suggested by Federico Beffa <beffa@fbengineering.ch>. * guix/monads.scm (bind-syntax): New macro. (with-monad): Use it instead of 'identifier-syntax'. * tests/monads.scm (">>= with more than two arguments"): New test. * doc/guix.texi (The Store Monad): Explain that there can be several MPROC. Add an example.
This commit is contained in:
parent
ae9b96c784
commit
751630c9c3
3 changed files with 56 additions and 7 deletions
|
@ -2773,12 +2773,25 @@ in @var{monad}.
|
||||||
Return a monadic value that encapsulates @var{val}.
|
Return a monadic value that encapsulates @var{val}.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Syntax} >>= @var{mval} @var{mproc}
|
@deffn {Scheme Syntax} >>= @var{mval} @var{mproc} ...
|
||||||
@dfn{Bind} monadic value @var{mval}, passing its ``contents'' to monadic
|
@dfn{Bind} monadic value @var{mval}, passing its ``contents'' to monadic
|
||||||
procedure @var{mproc}@footnote{This operation is commonly referred to as
|
procedures @var{mproc}@dots{}@footnote{This operation is commonly
|
||||||
``bind'', but that name denotes an unrelated procedure in Guile. Thus
|
referred to as ``bind'', but that name denotes an unrelated procedure in
|
||||||
we use this somewhat cryptic symbol inherited from the Haskell
|
Guile. Thus we use this somewhat cryptic symbol inherited from the
|
||||||
language.}.
|
Haskell language.}. There can be one @var{mproc} or several of them, as
|
||||||
|
in this example:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(run-with-state
|
||||||
|
(with-monad %state-monad
|
||||||
|
(>>= (return 1)
|
||||||
|
(lambda (x) (return (+ 1 x)))
|
||||||
|
(lambda (x) (return (* 2 x)))))
|
||||||
|
'some-state)
|
||||||
|
|
||||||
|
@result{} 4
|
||||||
|
@result{} some-state
|
||||||
|
@end example
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Syntax} mlet @var{monad} ((@var{var} @var{mval}) ...) @
|
@deffn {Scheme Syntax} mlet @var{monad} ((@var{var} @var{mval}) ...) @
|
||||||
|
|
|
@ -112,6 +112,29 @@ (define-syntax-parameter return
|
||||||
(lambda (s)
|
(lambda (s)
|
||||||
(syntax-violation 'return "return used outside of 'with-monad'" s)))
|
(syntax-violation 'return "return used outside of 'with-monad'" s)))
|
||||||
|
|
||||||
|
(define-syntax-rule (bind-syntax bind)
|
||||||
|
"Return a macro transformer that handles the expansion of '>>=' expressions
|
||||||
|
using BIND as the binary bind operator.
|
||||||
|
|
||||||
|
This macro exists to allow the expansion of n-ary '>>=' expressions, even
|
||||||
|
though BIND is simply binary, as in:
|
||||||
|
|
||||||
|
(with-monad %state-monad
|
||||||
|
(>>= (return 1)
|
||||||
|
(lift 1+ %state-monad)
|
||||||
|
(lift 1+ %state-monad)))
|
||||||
|
"
|
||||||
|
(lambda (stx)
|
||||||
|
(define (expand body)
|
||||||
|
(syntax-case body ()
|
||||||
|
((_ mval mproc)
|
||||||
|
#'(bind mval mproc))
|
||||||
|
((x mval mproc0 mprocs (... ...))
|
||||||
|
(expand #'(>>= (>>= mval mproc0)
|
||||||
|
mprocs (... ...))))))
|
||||||
|
|
||||||
|
(expand stx)))
|
||||||
|
|
||||||
(define-syntax with-monad
|
(define-syntax with-monad
|
||||||
(lambda (s)
|
(lambda (s)
|
||||||
"Evaluate BODY in the context of MONAD, and return its result."
|
"Evaluate BODY in the context of MONAD, and return its result."
|
||||||
|
@ -120,13 +143,13 @@ (define-syntax with-monad
|
||||||
(eq? 'macro (syntax-local-binding #'monad))
|
(eq? 'macro (syntax-local-binding #'monad))
|
||||||
;; MONAD is a syntax transformer, so we can obtain the bind and return
|
;; MONAD is a syntax transformer, so we can obtain the bind and return
|
||||||
;; methods by directly querying it.
|
;; methods by directly querying it.
|
||||||
#'(syntax-parameterize ((>>= (identifier-syntax (monad %bind)))
|
#'(syntax-parameterize ((>>= (bind-syntax (monad %bind)))
|
||||||
(return (identifier-syntax (monad %return))))
|
(return (identifier-syntax (monad %return))))
|
||||||
body ...))
|
body ...))
|
||||||
((_ monad body ...)
|
((_ monad body ...)
|
||||||
;; MONAD refers to the <monad> record that represents the monad at run
|
;; MONAD refers to the <monad> record that represents the monad at run
|
||||||
;; time, so use the slow method.
|
;; time, so use the slow method.
|
||||||
#'(syntax-parameterize ((>>= (identifier-syntax
|
#'(syntax-parameterize ((>>= (bind-syntax
|
||||||
(monad-bind monad)))
|
(monad-bind monad)))
|
||||||
(return (identifier-syntax
|
(return (identifier-syntax
|
||||||
(monad-return monad))))
|
(monad-return monad))))
|
||||||
|
|
|
@ -103,6 +103,19 @@ (define (g x)
|
||||||
%monads
|
%monads
|
||||||
%monad-run))
|
%monad-run))
|
||||||
|
|
||||||
|
(test-assert ">>= with more than two arguments"
|
||||||
|
(every (lambda (monad run)
|
||||||
|
(let ((1+ (lift1 1+ monad))
|
||||||
|
(2* (lift1 (cut * 2 <>) monad)))
|
||||||
|
(with-monad monad
|
||||||
|
(let ((number (random 777)))
|
||||||
|
(= (run (>>= (return number)
|
||||||
|
1+ 1+ 1+
|
||||||
|
2* 2* 2*))
|
||||||
|
(* 8 (+ number 3)))))))
|
||||||
|
%monads
|
||||||
|
%monad-run))
|
||||||
|
|
||||||
(test-assert "mbegin"
|
(test-assert "mbegin"
|
||||||
(every (lambda (monad run)
|
(every (lambda (monad run)
|
||||||
(with-monad monad
|
(with-monad monad
|
||||||
|
|
Loading…
Reference in a new issue