Next: G-Expressions, Previous: Деривации, Up: Программный интерфейс [Contents][Index]
Все процедуры, которые работают с store, описанные в предыдущих разделах, принимают открытое соединение с демоном сборки в качестве первого аргумента. Хотя основная модель является функциональной, они либо имеют побочные эффекты, либо зависят от текущего состояния store.
Первое неудобно: соединение с демоном сборки должно поддерживаться во всех этих функциях, что делает невозможным составление функций, которые не принимают этот параметр, с функциями, которые его принимают. Последнее может быть проблематичным: поскольку операции store имеют побочные эффекты и/или зависят от внешнего состояния, они должны быть правильно упорядочены.
Здесь на помощь приходит модуль (guix monads)
. Этот модуль
предоставляет основу для работы с monads и особенно полезную монаду
для наших целей - store monad. Монады - это конструкции, которая
позволяют две вещи: связывать “контекст” со значениями (в нашем случае
контекст - это store) и строить последовательности вычислений (здесь
вычисления включают доступ к store). Значения в монаде—значения, которые
несут этот дополнительный контекст—называются монадическими
значениями; процедуры, возвращающие такие значения, называются
монадическими процедурами.
Рассмотрим эту “нормальную” процедуру:
(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))))
Используя (guix monads)
и (guix gexp)
, ее можно переписать как
монадическую функцию:
(define (sh-symlink)
;; Same, but return a monadic value.
(mlet %store-monad ((drv (package->derivation bash)))
(gexp->derivation "sh"
#~(symlink (string-append #$drv "/bin/bash")
#$output))))
Во второй версии следует отметить несколько моментов: параметр store
теперь является неявным и является “threaded” в вызовах
package->derivation
и gexp->derivation
монадические процедуры,
а монадическим значением, возвращаемым package->derivation
, является
bound с использованием mlet
вместо простого let
.
Оказывается, вызов package->derivation
можно даже опустить, поскольку
она будет выполняться неявно, как мы увидим позже (see G-Expressions):
(define (sh-symlink)
(gexp->derivation "sh"
#~(symlink (string-append #$bash "/bin/bash")
#$output)))
Вызов монадического sh-symlink
ни на что не влияет. Как кто-то
однажды сказал: “Вы выходите из монады, как вы выходите из горящего здания:
by running”. Итак, чтобы выйти из монады и получить желаемый эффект, нужно
использовать run-with-store
:
(run-with-store (open-connection) (sh-symlink)) ⇒ /gnu/store/...-sh-symlink
Note that the (guix monad-repl)
module extends the Guile REPL with
new “commands” to make it easier to deal with monadic procedures:
run-in-store
, and enter-store-monad
(see Using Guix Interactively). The former is used to “run” a single monadic value
through the store:
scheme@(guile-user)> ,run-in-store (package->derivation hello) $1 = #<derivation /gnu/store/…-hello-2.9.drv => …>
Последний входит в рекурсивный REPL, где все возвращаемые значения автоматически проходят через хранилище:
scheme@(guile-user)> ,enter-store-monad store-monad@(guile-user) [1]> (package->derivation hello) $2 = #<derivation /gnu/store/…-hello-2.9.drv => …> store-monad@(guile-user) [1]> (text-file "foo" "Hello!") $3 = "/gnu/store/…-foo" store-monad@(guile-user) [1]> ,q scheme@(guile-user)>
Обратите внимание, что немонадические значения не могут быть возвращены в
REPL store-monad
.
Other meta-commands are available at the REPL, such as ,build
to
build a file-like object (see Using Guix Interactively).
Основные синтаксические формы для работы с монадами в целом предоставляются
модулем (guix monads)
и описаны ниже.
Evaluate any >>=
or return
forms in body as being in
monad.
Возвращает монадическое значение, инкапсулирующее val.
Bind monadic value mval, passing its “contents” to monadic procedures mproc…23. There can be one mproc or several of them, as in this example:
(run-with-state (with-monad %state-monad (>>= (return 1) (lambda (x) (return (+ 1 x))) (lambda (x) (return (* 2 x))))) 'some-state) ⇒ 4 ⇒ some-state
Bind the variables var to the monadic values mval in body,
which is a sequence of expressions. As with the bind operator, this can be
thought of as “unpacking” the raw, non-monadic value “contained” in
mval and making var refer to that raw, non-monadic value within
the scope of the body. The form (var -> val) binds
var to the “normal” value val, as per let
. The binding
operations occur in sequence from left to right. The last expression of
body must be a monadic expression, and its result will become the
result of the mlet
or mlet*
when run in the monad.
mlet*
is to mlet
what let*
is to let
(see Local Bindings in GNU Guile Reference Manual).
Bind mexp and the following monadic expressions in sequence, returning the result of the last expression. Every expression in the sequence must be a monadic expression.
This is akin to mlet
, except that the return values of the monadic
expressions are ignored. In that sense, it is analogous to begin
,
but applied to monadic expressions.
When condition is true, evaluate the sequence of monadic expressions
mexp0..mexp* as in an mbegin
. When condition is
false, return *unspecified*
in the current monad. Every expression
in the sequence must be a monadic expression.
When condition is false, evaluate the sequence of monadic expressions
mexp0..mexp* as in an mbegin
. When condition is
true, return *unspecified*
in the current monad. Every expression in
the sequence must be a monadic expression.
The (guix monads)
module provides the state monad, which allows
an additional value—the state—to be threaded through monadic
procedure calls.
The state monad. Procedures in the state monad can access and change the state that is threaded.
Consider the example below. The square
procedure returns a value in
the state monad. It returns the square of its argument, but also increments
the current state value:
(define (square x) (mlet %state-monad ((count (current-state))) (mbegin %state-monad (set-current-state (+ 1 count)) (return (* x x))))) (run-with-state (sequence %state-monad (map square (iota 3))) 0) ⇒ (0 1 4) ⇒ 3
When “run” through %state-monad
, we obtain that additional state
value, which is the number of square
calls.
Вернуть текущее состояние в виде монадического значения.
Set the current state to value and return the previous state as a monadic value.
Push value to the current state, which is assumed to be a list, and return the previous state as a monadic value.
Pop a value from the current state and return it as a monadic value. The state is assumed to be a list.
Run monadic value mval starting with state as the initial state. Return two values: the resulting value, and the resulting state.
The main interface to the store monad, provided by the (guix store)
module, is as follows.
The store monad—an alias for %state-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).
value in the store monad, in store, an open store connection.
Return as a monadic value the absolute file name in the store of the file containing text, a string. references is a list of store items that the resulting text file refers to; it defaults to the empty list.
Return as a monadic value the absolute file name in the store of the file containing data, a bytevector. references is a list of store items that the resulting binary file refers to; it defaults to the empty list.
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.
When recursive? is true, call (select? file
stat)
for each directory entry, where file is the entry’s
absolute file name and stat is the result of lstat
; exclude
entries for which select? does not return true.
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")
The (guix packages)
module exports the following package-related
monadic procedures:
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. When target is true, use it as a cross-compilation target triplet.
Note that this procedure does not build package. Thus, the result might or might not designate an existing file. We recommend not using this procedure unless you know what you are doing.
package-derivation
andpackage-cross-derivation
(see Описание пакетов).
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: G-Expressions, Previous: Деривации, Up: Программный интерфейс [Contents][Index]