2013-10-03 22:45:25 +02:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2015-01-12 23:26:52 +01:00
|
|
|
|
;;; Copyright © 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
|
2013-10-03 22:45:25 +02:00
|
|
|
|
;;;
|
|
|
|
|
;;; This file is part of GNU Guix.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
|
|
|
|
;;; under the terms of the GNU General Public License as published by
|
|
|
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
|
|
|
;;; your option) any later version.
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU Guix is distributed in the hope that it will be useful, but
|
|
|
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;;; GNU General Public License for more details.
|
|
|
|
|
;;;
|
|
|
|
|
;;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
(define-module (guix monads)
|
2013-10-02 21:58:19 +02:00
|
|
|
|
#:use-module ((system syntax)
|
|
|
|
|
#:select (syntax-local-binding))
|
2013-10-03 22:45:25 +02:00
|
|
|
|
#:use-module (ice-9 match)
|
2014-02-03 23:12:54 +01:00
|
|
|
|
#:use-module (srfi srfi-1)
|
2013-10-02 21:58:19 +02:00
|
|
|
|
#:use-module (srfi srfi-9)
|
2013-10-03 22:45:25 +02:00
|
|
|
|
#:use-module (srfi srfi-26)
|
|
|
|
|
#:export (;; Monads.
|
2013-10-02 21:58:19 +02:00
|
|
|
|
define-monad
|
2013-10-03 22:45:25 +02:00
|
|
|
|
monad?
|
|
|
|
|
monad-bind
|
|
|
|
|
monad-return
|
|
|
|
|
|
|
|
|
|
;; Syntax.
|
|
|
|
|
>>=
|
|
|
|
|
return
|
|
|
|
|
with-monad
|
|
|
|
|
mlet
|
|
|
|
|
mlet*
|
2014-10-08 23:35:08 +02:00
|
|
|
|
mbegin
|
2014-12-02 10:11:11 +01:00
|
|
|
|
mwhen
|
|
|
|
|
munless
|
2014-12-02 10:10:51 +01:00
|
|
|
|
lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
|
2013-10-03 22:45:25 +02:00
|
|
|
|
listm
|
|
|
|
|
foldm
|
|
|
|
|
mapm
|
|
|
|
|
sequence
|
|
|
|
|
anym
|
|
|
|
|
|
|
|
|
|
;; Concrete monads.
|
monads: Move '%store-monad' and related procedures where they belong.
This turns (guix monads) into a generic module for monads, and moves the
store monad and related monadic procedures in their corresponding
module.
* guix/monads.scm (store-return, store-bind, %store-monad, store-lift,
text-file, interned-file, package-file, package->derivation,
package->cross-derivation, origin->derivation, imported-modules,
compiled, modules, built-derivations, run-with-store): Move to...
* guix/store.scm (store-return, store-bind, %store-monad, store-lift,
text-file, interned-file): ... here.
(%guile-for-build): New variable.
(run-with-store): Moved from monads.scm. Remove default value for
#:guile-for-build.
* guix/packages.scm (default-guile): Export.
(set-guile-for-build): New procedure.
(package-file, package->derivation, package->cross-derivation,
origin->derivation): Moved from monads.scm.
* guix/derivations.scm (%guile-for-build): Remove.
(imported-modules): Rename to...
(%imported-modules): ... this.
(compiled-modules): Rename to...
(%compiled-modules): ... this.
(built-derivations, imported-modules, compiled-modules): New
procedures.
* gnu/services/avahi.scm, gnu/services/base.scm, gnu/services/dbus.scm,
gnu/services/dmd.scm, gnu/services/networking.scm,
gnu/services/ssh.scm, gnu/services/xorg.scm, gnu/system/install.scm,
gnu/system/linux-initrd.scm, gnu/system/shadow.scm, guix/download.scm,
guix/gexp.scm, guix/git-download.scm, guix/profiles.scm,
guix/svn-download.scm, tests/monads.scm: Adjust imports accordingly.
* guix/monad-repl.scm (default-guile-derivation): New procedure.
(store-monad-language, run-in-store): Use it.
* build-aux/hydra/gnu-system.scm (qemu-jobs): Add explicit
'set-guile-for-build' call.
* guix/scripts/archive.scm (derivation-from-expression): Likewise.
* guix/scripts/build.scm (options/resolve-packages): Likewise.
* guix/scripts/environment.scm (guix-environment): Likewise.
* guix/scripts/system.scm (guix-system): Likewise.
* doc/guix.texi (The Store Monad): Adjust module names accordingly.
2015-01-14 13:34:52 +01:00
|
|
|
|
%identity-monad))
|
2013-10-03 22:45:25 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;;
|
|
|
|
|
;;; This module implements the general mechanism of monads, and provides in
|
|
|
|
|
;;; particular an instance of the "store" monad. The API was inspired by that
|
|
|
|
|
;;; of Racket's "better-monads" module (see
|
|
|
|
|
;;; <http://planet.racket-lang.org/package-source/toups/functional.plt/1/1/planet-docs/better-monads-guide/index.html>).
|
|
|
|
|
;;; The implementation and use case were influenced by Oleg Kysielov's
|
|
|
|
|
;;; "Monadic Programming in Scheme" (see
|
|
|
|
|
;;; <http://okmij.org/ftp/Scheme/monad-in-Scheme.html>).
|
|
|
|
|
;;;
|
|
|
|
|
;;; The store monad allows us to (1) build sequences of operations in the
|
|
|
|
|
;;; store, and (2) make the store an implicit part of the execution context,
|
|
|
|
|
;;; rather than a parameter of every single function.
|
|
|
|
|
;;;
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
2013-10-02 21:58:19 +02:00
|
|
|
|
;; Record type for monads manipulated at run time.
|
|
|
|
|
(define-record-type <monad>
|
|
|
|
|
(make-monad bind return)
|
2013-10-03 22:45:25 +02:00
|
|
|
|
monad?
|
|
|
|
|
(bind monad-bind)
|
|
|
|
|
(return monad-return)) ; TODO: Add 'plus' and 'zero'
|
|
|
|
|
|
2013-10-02 21:58:19 +02:00
|
|
|
|
(define-syntax define-monad
|
|
|
|
|
(lambda (s)
|
|
|
|
|
"Define the monad under NAME, with the given bind and return methods."
|
|
|
|
|
(define prefix (string->symbol "% "))
|
|
|
|
|
(define (make-rtd-name name)
|
|
|
|
|
(datum->syntax name
|
|
|
|
|
(symbol-append prefix (syntax->datum name) '-rtd)))
|
|
|
|
|
|
|
|
|
|
(syntax-case s (bind return)
|
|
|
|
|
((_ name (bind b) (return r))
|
|
|
|
|
(with-syntax ((rtd (make-rtd-name #'name)))
|
|
|
|
|
#`(begin
|
|
|
|
|
(define rtd
|
|
|
|
|
;; The record type, for use at run time.
|
|
|
|
|
(make-monad b r))
|
|
|
|
|
|
|
|
|
|
(define-syntax name
|
|
|
|
|
;; An "inlined record", for use at expansion time. The goal is
|
|
|
|
|
;; to allow 'bind' and 'return' to be resolved at expansion
|
|
|
|
|
;; time, in the common case where the monad is accessed
|
|
|
|
|
;; directly as NAME.
|
|
|
|
|
(lambda (s)
|
|
|
|
|
(syntax-case s (%bind %return)
|
|
|
|
|
((_ %bind) #'b)
|
|
|
|
|
((_ %return) #'r)
|
|
|
|
|
(_ #'rtd))))))))))
|
|
|
|
|
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(define-syntax-parameter >>=
|
|
|
|
|
;; The name 'bind' is already taken, so we choose this (obscure) symbol.
|
|
|
|
|
(lambda (s)
|
|
|
|
|
(syntax-violation '>>= ">>= (bind) used outside of 'with-monad'" s)))
|
|
|
|
|
|
|
|
|
|
(define-syntax-parameter return
|
|
|
|
|
(lambda (s)
|
|
|
|
|
(syntax-violation 'return "return used outside of 'with-monad'" s)))
|
|
|
|
|
|
|
|
|
|
(define-syntax with-monad
|
|
|
|
|
(lambda (s)
|
|
|
|
|
"Evaluate BODY in the context of MONAD, and return its result."
|
|
|
|
|
(syntax-case s ()
|
|
|
|
|
((_ monad body ...)
|
2013-10-02 21:58:19 +02:00
|
|
|
|
(eq? 'macro (syntax-local-binding #'monad))
|
|
|
|
|
;; MONAD is a syntax transformer, so we can obtain the bind and return
|
|
|
|
|
;; methods by directly querying it.
|
|
|
|
|
#'(syntax-parameterize ((>>= (identifier-syntax (monad %bind)))
|
|
|
|
|
(return (identifier-syntax (monad %return))))
|
|
|
|
|
body ...))
|
|
|
|
|
((_ monad body ...)
|
|
|
|
|
;; MONAD refers to the <monad> record that represents the monad at run
|
|
|
|
|
;; time, so use the slow method.
|
2013-10-03 22:45:25 +02:00
|
|
|
|
#'(syntax-parameterize ((>>= (identifier-syntax
|
|
|
|
|
(monad-bind monad)))
|
|
|
|
|
(return (identifier-syntax
|
|
|
|
|
(monad-return monad))))
|
|
|
|
|
body ...)))))
|
|
|
|
|
|
|
|
|
|
(define-syntax mlet*
|
|
|
|
|
(syntax-rules (->)
|
|
|
|
|
"Bind the given monadic values MVAL to the given variables VAR. When the
|
|
|
|
|
form is (VAR -> VAL), bind VAR to the non-monadic value VAL in the same way as
|
|
|
|
|
'let'."
|
|
|
|
|
;; Note: the '->' symbol corresponds to 'is:' in 'better-monads.rkt'.
|
|
|
|
|
((_ monad () body ...)
|
|
|
|
|
(with-monad monad body ...))
|
|
|
|
|
((_ monad ((var mval) rest ...) body ...)
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(>>= mval
|
|
|
|
|
(lambda (var)
|
|
|
|
|
(mlet* monad (rest ...)
|
|
|
|
|
body ...)))))
|
|
|
|
|
((_ monad ((var -> val) rest ...) body ...)
|
|
|
|
|
(let ((var val))
|
|
|
|
|
(mlet* monad (rest ...)
|
|
|
|
|
body ...)))))
|
|
|
|
|
|
|
|
|
|
(define-syntax mlet
|
|
|
|
|
(lambda (s)
|
|
|
|
|
(syntax-case s ()
|
|
|
|
|
((_ monad ((var mval ...) ...) body ...)
|
|
|
|
|
(with-syntax (((temp ...) (generate-temporaries #'(var ...))))
|
|
|
|
|
#'(mlet* monad ((temp mval ...) ...)
|
|
|
|
|
(let ((var temp) ...)
|
|
|
|
|
body ...)))))))
|
|
|
|
|
|
2014-10-08 23:35:08 +02:00
|
|
|
|
(define-syntax mbegin
|
2014-12-02 10:11:11 +01:00
|
|
|
|
(syntax-rules (%current-monad)
|
2014-10-08 23:35:08 +02:00
|
|
|
|
"Bind the given monadic expressions in sequence, returning the result of
|
|
|
|
|
the last one."
|
2014-12-02 10:11:11 +01:00
|
|
|
|
((_ %current-monad mexp)
|
|
|
|
|
mexp)
|
|
|
|
|
((_ %current-monad mexp rest ...)
|
|
|
|
|
(>>= mexp
|
|
|
|
|
(lambda (unused-value)
|
|
|
|
|
(mbegin %current-monad rest ...))))
|
2014-10-08 23:35:08 +02:00
|
|
|
|
((_ monad mexp)
|
|
|
|
|
(with-monad monad
|
|
|
|
|
mexp))
|
|
|
|
|
((_ monad mexp rest ...)
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(>>= mexp
|
|
|
|
|
(lambda (unused-value)
|
|
|
|
|
(mbegin monad rest ...)))))))
|
|
|
|
|
|
2014-12-02 10:11:11 +01:00
|
|
|
|
(define-syntax mwhen
|
|
|
|
|
(syntax-rules ()
|
|
|
|
|
"When CONDITION is true, evaluate EXP0..EXP* as in an 'mbegin'. When
|
|
|
|
|
CONDITION is false, return *unspecified* in the current monad."
|
|
|
|
|
((_ condition exp0 exp* ...)
|
|
|
|
|
(if condition
|
|
|
|
|
(mbegin %current-monad
|
|
|
|
|
exp0 exp* ...)
|
|
|
|
|
(return *unspecified*)))))
|
|
|
|
|
|
|
|
|
|
(define-syntax munless
|
|
|
|
|
(syntax-rules ()
|
|
|
|
|
"When CONDITION is false, evaluate EXP0..EXP* as in an 'mbegin'. When
|
|
|
|
|
CONDITION is true, return *unspecified* in the current monad."
|
|
|
|
|
((_ condition exp0 exp* ...)
|
|
|
|
|
(if condition
|
|
|
|
|
(return *unspecified*)
|
|
|
|
|
(mbegin %current-monad
|
|
|
|
|
exp0 exp* ...)))))
|
|
|
|
|
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(define-syntax define-lift
|
|
|
|
|
(syntax-rules ()
|
|
|
|
|
((_ liftn (args ...))
|
|
|
|
|
(define (liftn proc monad)
|
|
|
|
|
"Lift PROC to MONAD---i.e., return a monadic function in MONAD."
|
|
|
|
|
(lambda (args ...)
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(return (proc args ...))))))))
|
|
|
|
|
|
2014-12-02 10:10:51 +01:00
|
|
|
|
(define-lift lift0 ())
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(define-lift lift1 (a))
|
|
|
|
|
(define-lift lift2 (a b))
|
|
|
|
|
(define-lift lift3 (a b c))
|
|
|
|
|
(define-lift lift4 (a b c d))
|
|
|
|
|
(define-lift lift5 (a b c d e))
|
|
|
|
|
(define-lift lift6 (a b c d e f))
|
|
|
|
|
(define-lift lift7 (a b c d e f g))
|
|
|
|
|
|
2014-11-05 22:25:09 +01:00
|
|
|
|
(define (lift proc monad)
|
|
|
|
|
"Lift PROC, a procedure that accepts an arbitrary number of arguments, to
|
|
|
|
|
MONAD---i.e., return a monadic function in MONAD."
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(lambda args
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(return (apply proc args)))))
|
|
|
|
|
|
|
|
|
|
(define (foldm monad mproc init lst)
|
|
|
|
|
"Fold MPROC over LST, a list of monadic values in MONAD, and return a
|
|
|
|
|
monadic value seeded by INIT."
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(let loop ((lst lst)
|
|
|
|
|
(result init))
|
|
|
|
|
(match lst
|
|
|
|
|
(()
|
|
|
|
|
(return result))
|
|
|
|
|
((head tail ...)
|
|
|
|
|
(mlet* monad ((item head)
|
|
|
|
|
(result (mproc item result)))
|
|
|
|
|
(loop tail result)))))))
|
|
|
|
|
|
|
|
|
|
(define (mapm monad mproc lst)
|
|
|
|
|
"Map MPROC over LST, a list of monadic values in MONAD, and return a monadic
|
2014-07-12 17:16:36 +02:00
|
|
|
|
list. LST items are bound from left to right, so effects in MONAD are known
|
|
|
|
|
to happen in that order."
|
|
|
|
|
(mlet monad ((result (foldm monad
|
|
|
|
|
(lambda (item result)
|
|
|
|
|
(mlet monad ((item (mproc item)))
|
|
|
|
|
(return (cons item result))))
|
|
|
|
|
'()
|
|
|
|
|
lst)))
|
|
|
|
|
(return (reverse result))))
|
2013-10-03 22:45:25 +02:00
|
|
|
|
|
|
|
|
|
(define-inlinable (sequence monad lst)
|
|
|
|
|
"Turn the list of monadic values LST into a monadic list of values, by
|
|
|
|
|
evaluating each item of LST in sequence."
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(mapm monad return lst)))
|
|
|
|
|
|
|
|
|
|
(define (anym monad proc lst)
|
|
|
|
|
"Apply PROC to the list of monadic values LST; return the first value,
|
|
|
|
|
lifted in MONAD, for which PROC returns true."
|
|
|
|
|
(with-monad monad
|
|
|
|
|
(let loop ((lst lst))
|
|
|
|
|
(match lst
|
|
|
|
|
(()
|
|
|
|
|
(return #f))
|
|
|
|
|
((head tail ...)
|
2013-12-09 21:10:28 +01:00
|
|
|
|
(mlet* monad ((value head)
|
|
|
|
|
(result -> (proc value)))
|
|
|
|
|
(if result
|
|
|
|
|
(return result)
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(loop tail))))))))
|
|
|
|
|
|
|
|
|
|
(define-syntax listm
|
|
|
|
|
(lambda (s)
|
|
|
|
|
"Return a monadic list in MONAD from the monadic values MVAL."
|
|
|
|
|
(syntax-case s ()
|
|
|
|
|
((_ monad mval ...)
|
|
|
|
|
(with-syntax (((val ...) (generate-temporaries #'(mval ...))))
|
|
|
|
|
#'(mlet monad ((val mval) ...)
|
|
|
|
|
(return (list val ...))))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Identity monad.
|
|
|
|
|
;;;
|
|
|
|
|
|
2013-10-02 21:58:19 +02:00
|
|
|
|
(define-inlinable (identity-return value)
|
2013-10-03 22:45:25 +02:00
|
|
|
|
value)
|
|
|
|
|
|
2013-10-02 21:58:19 +02:00
|
|
|
|
(define-inlinable (identity-bind mvalue mproc)
|
2013-10-03 22:45:25 +02:00
|
|
|
|
(mproc mvalue))
|
|
|
|
|
|
2013-10-02 21:58:19 +02:00
|
|
|
|
(define-monad %identity-monad
|
|
|
|
|
(bind identity-bind)
|
|
|
|
|
(return identity-return))
|
2013-10-03 22:45:25 +02:00
|
|
|
|
|
|
|
|
|
;;; monads.scm end here
|