1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of GNU Guix.
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (guix monads)
20 #:use-module (guix store)
21 #:use-module (guix derivations)
22 #:use-module (guix packages)
23 #:use-module ((system syntax)
24 #:select (syntax-local-binding))
25 #:use-module (ice-9 match)
26 #:use-module (srfi srfi-1)
27 #:use-module (srfi srfi-9)
28 #:use-module (srfi srfi-26)
41 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
63 #:replace (imported-modules
68 ;;; This module implements the general mechanism of monads, and provides in
69 ;;; particular an instance of the "store" monad. The API was inspired by that
70 ;;; of Racket's "better-monads" module (see
71 ;;; <http://planet.racket-lang.org/package-source/toups/functional.plt/1/1/planet-docs/better-monads-guide/index.html>).
72 ;;; The implementation and use case were influenced by Oleg Kysielov's
73 ;;; "Monadic Programming in Scheme" (see
74 ;;; <http://okmij.org/ftp/Scheme/monad-in-Scheme.html>).
76 ;;; The store monad allows us to (1) build sequences of operations in the
77 ;;; store, and (2) make the store an implicit part of the execution context,
78 ;;; rather than a parameter of every single function.
82 ;; Record type for monads manipulated at run time.
83 (define-record-type <monad>
84 (make-monad bind return)
87 (return monad-return)) ; TODO: Add 'plus' and 'zero'
89 (define-syntax define-monad
91 "Define the monad under NAME, with the given bind and return methods."
92 (define prefix (string->symbol "% "))
93 (define (make-rtd-name name)
95 (symbol-append prefix (syntax->datum name) '-rtd)))
97 (syntax-case s (bind return)
98 ((_ name (bind b) (return r))
99 (with-syntax ((rtd (make-rtd-name #'name)))
102 ;; The record type, for use at run time.
106 ;; An "inlined record", for use at expansion time. The goal is
107 ;; to allow 'bind' and 'return' to be resolved at expansion
108 ;; time, in the common case where the monad is accessed
111 (syntax-case s (%bind %return)
116 (define-syntax-parameter >>=
117 ;; The name 'bind' is already taken, so we choose this (obscure) symbol.
119 (syntax-violation '>>= ">>= (bind) used outside of 'with-monad'" s)))
121 (define-syntax-parameter return
123 (syntax-violation 'return "return used outside of 'with-monad'" s)))
125 (define-syntax with-monad
127 "Evaluate BODY in the context of MONAD, and return its result."
130 (eq? 'macro (syntax-local-binding #'monad))
131 ;; MONAD is a syntax transformer, so we can obtain the bind and return
132 ;; methods by directly querying it.
133 #'(syntax-parameterize ((>>= (identifier-syntax (monad %bind)))
134 (return (identifier-syntax (monad %return))))
137 ;; MONAD refers to the <monad> record that represents the monad at run
138 ;; time, so use the slow method.
139 #'(syntax-parameterize ((>>= (identifier-syntax
141 (return (identifier-syntax
142 (monad-return monad))))
147 "Bind the given monadic values MVAL to the given variables VAR. When the
148 form is (VAR -> VAL), bind VAR to the non-monadic value VAL in the same way as
150 ;; Note: the '->' symbol corresponds to 'is:' in 'better-monads.rkt'.
151 ((_ monad () body ...)
152 (with-monad monad body ...))
153 ((_ monad ((var mval) rest ...) body ...)
157 (mlet* monad (rest ...)
159 ((_ monad ((var -> val) rest ...) body ...)
161 (mlet* monad (rest ...)
167 ((_ monad ((var mval ...) ...) body ...)
168 (with-syntax (((temp ...) (generate-temporaries #'(var ...))))
169 #'(mlet* monad ((temp mval ...) ...)
170 (let ((var temp) ...)
173 (define-syntax define-lift
175 ((_ liftn (args ...))
176 (define (liftn proc monad)
177 "Lift PROC to MONAD---i.e., return a monadic function in MONAD."
180 (return (proc args ...))))))))
182 (define-lift lift1 (a))
183 (define-lift lift2 (a b))
184 (define-lift lift3 (a b c))
185 (define-lift lift4 (a b c d))
186 (define-lift lift5 (a b c d e))
187 (define-lift lift6 (a b c d e f))
188 (define-lift lift7 (a b c d e f g))
190 (define (lift nargs proc monad)
191 "Lift PROC, a procedure that accepts NARGS arguments, to MONAD---i.e.,
192 return a monadic function in MONAD."
195 (return (apply proc args)))))
197 (define (foldm monad mproc init lst)
198 "Fold MPROC over LST, a list of monadic values in MONAD, and return a
199 monadic value seeded by INIT."
207 (mlet* monad ((item head)
208 (result (mproc item result)))
209 (loop tail result)))))))
211 (define (mapm monad mproc lst)
212 "Map MPROC over LST, a list of monadic values in MONAD, and return a monadic
213 list. LST items are bound from left to right, so effects in MONAD are known
214 to happen in that order."
215 (mlet monad ((result (foldm monad
216 (lambda (item result)
217 (mlet monad ((item (mproc item)))
218 (return (cons item result))))
221 (return (reverse result))))
223 (define-inlinable (sequence monad lst)
224 "Turn the list of monadic values LST into a monadic list of values, by
225 evaluating each item of LST in sequence."
227 (mapm monad return lst)))
229 (define (anym monad proc lst)
230 "Apply PROC to the list of monadic values LST; return the first value,
231 lifted in MONAD, for which PROC returns true."
233 (let loop ((lst lst))
238 (mlet* monad ((value head)
239 (result -> (proc value)))
246 "Return a monadic list in MONAD from the monadic values MVAL."
249 (with-syntax (((val ...) (generate-temporaries #'(mval ...))))
250 #'(mlet monad ((val mval) ...)
251 (return (list val ...))))))))
259 (define-inlinable (identity-return value)
262 (define-inlinable (identity-bind mvalue mproc)
265 (define-monad %identity-monad
267 (return identity-return))
274 ;; return:: a -> StoreM a
275 (define-inlinable (store-return value)
276 "Return VALUE from a monadic function."
277 ;; The monadic value is just this.
281 ;; >>=:: StoreM a -> (a -> StoreM b) -> StoreM b
282 (define-inlinable (store-bind mvalue mproc)
283 "Bind MVALUE in MPROC."
285 (let* ((value (mvalue store))
286 (mresult (mproc value)))
289 (define-monad %store-monad
291 (return store-return))
294 (define (store-lift proc)
295 "Lift PROC, a procedure whose first argument is a connection to the store,
300 (apply proc store args))))
302 (set-object-property! result 'documentation
303 (procedure-property proc 'documentation))
307 ;;; Store monad operators.
310 (define* (text-file name text)
311 "Return as a monadic value the absolute file name in the store of the file
312 containing TEXT, a string."
314 (add-text-to-store store name text '())))
316 (define* (text-file* name #:rest text)
317 "Return as a monadic value a derivation that builds a text file containing
318 all of TEXT. TEXT may list, in addition to strings, packages, derivations,
319 and store file names; the resulting store file holds references to all these."
321 ;; Transform packages and derivations from TEXT into a valid input list.
322 (filter-map (match-lambda
323 ((? package? p) `("x" ,p))
324 ((? derivation? d) `("x" ,d))
327 (and (direct-store-path? s) `("x" ,s)))
331 (define (computed-text text inputs)
332 ;; Using the lowered INPUTS, return TEXT with derivations replaced with
333 ;; their output file name.
334 (define (real-string? s)
335 (and (string? s) (not (direct-store-path? s))))
337 (let loop ((inputs inputs)
342 (string-concatenate-reverse result))
343 (((? real-string? head) rest ...)
344 (loop inputs rest (cons head result)))
347 (((_ (? derivation? drv) sub-drv ...) inputs ...)
349 (cons (apply derivation->output-path drv
352 (((_ file) inputs ...)
353 ;; FILE is the result of 'add-text-to-store' or so.
354 (loop inputs rest (cons file result))))))))
356 (define (builder inputs)
357 `(call-with-output-file (assoc-ref %outputs "out")
359 (display ,(computed-text text inputs) port))))
361 ;; TODO: Rewrite using 'gexp->derivation'.
362 (mlet %store-monad ((inputs (lower-inputs inputs)))
363 (derivation-expression name (builder inputs)
366 (define* (interned-file file #:optional name
367 #:key (recursive? #t))
368 "Return the name of FILE once interned in the store. Use NAME as its store
369 name, or the basename of FILE if NAME is omitted.
371 When RECURSIVE? is true, the contents of FILE are added recursively; if FILE
372 designates a flat file and RECURSIVE? is true, its contents are added, and its
373 permission bits are kept."
375 (add-to-store store (or name (basename file))
376 recursive? "sha256" file)))
378 (define* (package-file package
380 #:key (system (%current-system)) (output "out"))
381 "Return as a monadic value the absolute file name of FILE within the
382 OUTPUT directory of PACKAGE. When FILE is omitted, return the name of the
383 OUTPUT directory of PACKAGE."
385 (let* ((drv (package-derivation store package system))
386 (out (derivation->output-path drv output)))
388 (string-append out "/" file)
391 (define (lower-inputs inputs)
392 "Turn any package from INPUTS into a derivation; return the corresponding
393 input list as a monadic value."
394 ;; XXX: This procedure is bound to disappear with 'derivation-expression'.
395 (with-monad %store-monad
396 (sequence %store-monad
398 ((name (? package? package) sub-drv ...)
399 (mlet %store-monad ((drv (package->derivation package)))
400 (return `(,name ,drv ,@sub-drv))))
401 ((name (? string? file))
402 (return `(,name ,file)))
407 (define derivation-expression
408 ;; XXX: This procedure is superseded by 'gexp->derivation'.
409 (store-lift build-expression->derivation))
411 (define package->derivation
412 (store-lift package-derivation))
414 (define origin->derivation
415 (store-lift package-source-derivation))
417 (define imported-modules
418 (store-lift (@ (guix derivations) imported-modules)))
420 (define compiled-modules
421 (store-lift (@ (guix derivations) compiled-modules)))
423 (define built-derivations
424 (store-lift build-derivations))
426 (define* (run-with-store store mval
428 (guile-for-build (%guile-for-build))
429 (system (%current-system)))
430 "Run MVAL, a monadic value in the store monad, in STORE, an open store
432 (define (default-guile)
433 ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
434 ;; modules directly, to avoid circular dependencies, hence this hack.
435 (module-ref (resolve-interface '(gnu packages base))
438 (parameterize ((%guile-for-build (or guile-for-build
439 (package-derivation store
442 (%current-system system))
445 ;;; monads.scm end here