Next: , Previous: , Up: Programming Interface   [Contents][Index]


4.5 The Store Monad

The procedures that operate on the store described in the previous sections all take an open connection to the build daemon as their first argument. Although the underlying model is functional, they either have side effects or depend on the current state of the store.

The former is inconvenient: the connection to the build daemon has to be carried around in all those functions, making it impossible to compose functions that do not take that parameter with functions that do. The latter can be problematic: since store operations have side effects and/or depend on external state, they have to be properly sequenced.

This is where the (guix monads) module comes in. This module provides a framework for working with monads, and a particularly useful monad for our uses, the store monad. Monads are a construct that allows two things: associating “context” with values (in our case, the context is the store), and building sequences of computations (here computations includes accesses to the store.) Values in a monad—values that carry this additional context—are called monadic values; procedures that return such values are called monadic procedures.

Consider this “normal” procedure:

(define (sh-symlink store)
  ;; Return a derivation that symlinks the 'bash' executable.
  (let* ((drv (package-derivation store bash))
         (out (derivation->output-path drv))
         (sh  (string-append out "/bin/bash")))
    (build-expression->derivation store "sh"
                                  `(symlink ,sh %output))))

Using (guix monads), it may be rewritten as a monadic function:

(define (sh-symlink)
  ;; Same, but return a monadic value.
  (gexp->derivation "sh"
                    #~(symlink (string-append #$bash "/bin/bash") #$output)))

There are two things to note in the second version: the store parameter is now implicit, and the monadic value returned by package-file—a wrapper around package-derivation and derivation->output-path—is bound using mlet instead of plain let.

Calling the monadic profile.sh has no effect. To get the desired effect, one must use run-with-store:

(run-with-store (open-connection) (profile.sh))
⇒ /gnu/store/...-profile.sh

The main syntactic forms to deal with monads in general are described below.

Scheme Syntax: with-monad monad body ...

Evaluate any >>= or return forms in body as being in monad.

Scheme Syntax: return val

Return a monadic value that encapsulates val.

Scheme Syntax: >>= mval mproc

Bind monadic value mval, passing its “contents” to monadic procedure mproc5.

Scheme Syntax: mlet monad ((var mval) ...) body ...
Scheme Syntax: mlet* monad ((var mval) ...) body ...

Bind the variables var to the monadic values mval in body. The form (var -> val) binds var to the “normal” value val, as per let.

mlet* is to mlet what let* is to let (see Local Bindings in GNU Guile Reference Manual).

The interface to the store monad provided by (guix monads) is as follows.

Scheme Variable: %store-monad

The store monad. Values in the store monad encapsulate accesses to the store. When its effect is needed, a value of the store monad must be “evaluated” by passing it to the run-with-store procedure (see below.)

Scheme Procedure: run-with-store store mval [#:guile-for-build] [#:system (%current-system)]

Run mval, a monadic value in the store monad, in store, an open store connection.

Monadic Procedure: text-file name text

Return as a monadic value the absolute file name in the store of the file containing text, a string.

Monadic Procedure: text-file* name text

Return as a monadic value a derivation that builds a text file containing all of text. text may list, in addition to strings, packages, derivations, and store file names; the resulting store file holds references to all these.

This variant should be preferred over text-file anytime the file to create will reference items from the store. This is typically the case when building a configuration file that embeds store file names, like this:

(define (profile.sh)
  ;; Return the name of a shell script in the store that
  ;; initializes the 'PATH' environment variable.
  (text-file* "profile.sh"
              "export PATH=" coreutils "/bin:"
              grep "/bin:" sed "/bin\n"))

In this example, the resulting /gnu/store/…-profile.sh file will references coreutils, grep, and sed, thereby preventing them from being garbage-collected during its lifetime.

Monadic Procedure: interned-file file [name] [#:recursive? #t]

Return the name of file once interned in the store. Use name as its store name, or the basename of file if name is omitted.

When recursive? is true, the contents of file are added recursively; if file designates a flat file and recursive? is true, its contents are added, and its permission bits are kept.

The example below adds a file to the store, under two different names:

(run-with-store (open-connection)
  (mlet %store-monad ((a (interned-file "README"))
                      (b (interned-file "README" "LEGU-MIN")))
    (return (list a b))))

⇒ ("/gnu/store/rwm…-README" "/gnu/store/44i…-LEGU-MIN")
Monadic Procedure: package-file package [file] [#:system (%current-system)] [#:output "out"] Return as a monadic

value in the absolute file name of file within the output directory of package. When file is omitted, return the name of the output directory of package.

Monadic Procedure: package->derivation package [system]

Monadic version of package-derivation (see Defining Packages).


Footnotes

(5)

This operation is commonly referred to as “bind”, but that name denotes an unrelated procedure in Guile. Thus we use this somewhat cryptic symbol inherited from the Haskell language.


Next: , Previous: , Up: Programming Interface   [Contents][Index]