Next: , Previous: , Up: Interfaz programática   [Contents][Index]


8.11 La mónada del almacén

Los procedimientos que operan en el almacén descritos en la sección previa toman todos una conexión abierta al daemon de construcción en su primer parámetro. Aunque el modelo subyacente es funcional, tienen o bien efectos secundarios o dependen del estado actual del almacén.

Lo anterior es inconveniente: la conexión al daemon de construcción tiene que proporcionarse en todas estas funciones, haciendo imposible la composición de funciones que no toman ese parámetro con funciones que sí lo hacen. Lo último puede ser problemático: ya que las operaciones del almacén tienen efectos secundarios y/o dependen del estado externo, deben ser secuenciadas de manera adecuada.

Aquí es donde entra en juego el módulo (guix monads). Este módulo proporciona un entorno para trabajar con mónadas, y una mónada particularmente útil para nuestros usos, la mónada del almacén. Las mónadas son una construcción que permite dos cosas: asociar “contexto” con valores (en nuestro caso, el contexto es el almacén), y la construcción de secuencias de computaciones (aquí computaciones incluye accesos al almacén). Los valores en una mónada—valores que transportan este contexto adicional—se llaman valores monádicos; los procedimientos que devuelven dichos valores se llaman procedimientos monádicos.

Considere este procedimiento “normal”:

(define (enlace-sh almacen)
  ;; Devuelve una derivación que enlaza el ejecutable 'bash'.
  (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))))

Mediante el uso de (guix monads) y (guix gexp), puede reescribirse como una función monádica:

(define (enlace-sh)
  ;; Lo mismo, pero devuelve un valor monádico.
  (mlet %store-monad ((drv (package->derivation bash)))
    (gexp->derivation "sh"
                      #~(symlink (string-append #$drv "/bin/bash")
                                 #$output))))

Hay varias cosas a tener en cuenta en la segunda versión: el parámetro store ahora es implícito y es “hilado en las llamadas a los procedimientos monádicos package->derivation y gexp->derivation, y el valor monádico devuelto por package->derivation es asociado mediante el uso de mlet en vez de un simple let.

Al final, la llamada a package->derivation puede omitirse ya que tendrá lugar implícitamente, como veremos más adelante (see Expresiones-G):

(define (enlace-sh)
  (gexp->derivation "sh"
                    #~(symlink (string-append #$bash "/bin/bash")
                               #$output)))

La ejecución del procedimiento monádico enlace-para-sh no tiene ningún efecto. Como alguien dijo una vez, “sales de una mónada como sales de un edificio en llamas: corriendo” (run en inglés). Por tanto, para salir de la mónada y obtener el efecto deseado se debe usar run-with-store:

(run-with-store (open-connection) (enlace-sh))
 /gnu/store/...-enlace-para-sh

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 => …>

El último entra en un entorno interactivo recursivo, donde todos los valores devueltos se ejecutan automáticamente a través del almacén:

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)>

Fíjese que los valores no-monádicos no pueden devolverse en el entorno interactivo store-monad.

Other meta-commands are available at the REPL, such as ,build to build a file-like object (see Using Guix Interactively).

Las formas sintácticas principales para tratar con mónadas en general se proporcionan por el módulo (guix monads) y se describen a continuación.

Macro: with-monad monad body …

Evalúa cualquier forma >>= o return en cuerpo como estando en mónada.

Macro: return val

Devuelve el valor monádico que encapsula val.

Macro: >>= mval mproc …

Asocia el valor monádico mval, pasando su “contenido” a los procedimientos monádicos mproc25. Puede haber un mproc o varios, como en este ejemplo:

(run-with-state
    (with-monad %state-monad
      (>>= (return 1)
           (lambda (x) (return (+ 1 x)))
           (lambda (x) (return (* 2 x)))))
  'un-estado)

 4
 un-estado
Macro: mlet mónada ((var mval) …) cuerpo …
Macro: mlet* mónada ((var mval) …) cuerpo …

Asocia las variables var a los valores monádicos mval en cuerpo, el cual es una secuencia de expresiones. Como con el operador bind, esto puede pensarse como el “desempaquetado” del valor crudo no-monádico dentro del ámbito del cuerpo. La forma (var -> val) asocia var al valor “normal” val, como en let. Las operaciones de asociación ocurren en secuencia de izquierda a derecha. La última expresión de cuerpo debe ser una expresión monádica, y su resultado se convertirá en el resultado de mlet o mlet* cuando se ejecute en la mónada.

mlet* es a mlet lo que let* es a let (see Local Bindings in GNU Guile Reference Manual).

Macro: mbegin monad mexp …

Asocia mexp y las siguientes expresiones monádicas en secuencia, devolviendo el resultado de la última expresión. Cada expresión en la secuencia debe ser una expresión monádica.

Esto es similar a mlet, excepto que los valores devueltos por las expresiones monádicas se ignoran. En ese sentido el funcionamiento es análogo a begin pero aplicado a expresiones monádicas.

Macro: mwhen condition mexp0 mexp* …

Cuando condición es verdadero, evalúa la secuencia de expresiones monádicas mexp0..mexp* como dentro de mbegin. Cuando condición es falso, devuelve *unespecified* en la mónada actual. Todas las expresiones en la secuencia deben ser expresiones monádicas.

Macro: munless condition mexp0 mexp* …

Cuando condición es falso, evalúa la secuencia de expresiones monádicas mexp0..mexp* como dentro de mbegin. Cuando condición es verdadero, devuelve *unespecified* en la mónada actual. Todas las expresiones en la secuencia deben ser expresiones monádicas.

El módulo (guix monads) proporciona la mónada de estado, que permite que un valor adicional—el estado—sea hilado a través de las llamadas a procedimientos monádicos.

Variable: %state-monad

La mónada de estado. Procedimientos en la mónada de estado pueden acceder y cambiar el estado hilado.

Considere el siguiente ejemplo. El procedimiento cuadrado devuelve un valor en la mónada de estado.

(define (cuadrado 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 cuadrado (iota 3))) 0)
 (0 1 4)
 3

Cuando se “ejecuta” a través de %state-monad, obtenemos un valor adicional de estado, que es el número de llamadas a cuadrado.

Procedimiento monádico: current-state

Devuelve el estado actual como un valor monádico.

Procedimiento monádico: set-current-state valor

Establece el estado actual a valor y devuelve el estado previo como un valor monádico.

Procedimiento monádico: state-push valor

Apila valor al estado actual, que se asume que es una lista, y devuelve el estado previo como un valor monádico.

Procedimiento monádico: state-pop

Extrae un valor del estado actual y lo devuelve como un valor monádico. Se asume que el estado es una lista.

Procedimiento: run-with-state mval [estado]

Ejecuta un valor monádico mval comenzando con estado como el estado inicial. Devuelve dos valores: el valor resultante y el estado resultante.

La interfaz principal a la mónada del almacén, proporcionada por el módulo (guix store), es como sigue.

Variable: %store-monad

La mónada del almacén—un alias para %state-monad.

Los valores en la mónada del almacén encapsulan los accesos al almacén. Cuando su efecto es necesario, un valor de la mónada del almacén será “evaluado” cuando se proporcione al procedimiento run-with-store (véase a continuación).

Procedimiento: run-with-store almacén mval [#:guile-for-build] [#:system (%current-system)] Ejecuta mval, un

valor monádico en la mónada del almacén, en almacén, una conexión abierta al almacén.

Procedimiento monádico: text-file nombre texto [referencias]

Devuelve como un valor monádico el nombre absoluto del archivo en el almacén del archivo que contiene ŧexto, una cadena. referencias es una lista de elementos del almacén a los que el archivo de texto referencia; su valor predeterminado es la lista vacía.

Procedimiento monádico: binary-file nombre datos [referencias]

Devuelve como un valor monádico el nombre absoluto del archivo en el almacén del archivo que contiene datos, un vector de bytes. referencias es una lista de elementos del almacén a los que el archivo binario referencia; su valor predeterminado es la lista vacía.

Procedimiento monádico: interned-file archivo [nombre] [#:recursive? #t] [#:select? (const #t)]

Devuelve el nombre del archivo una vez internado en el almacén. Usa nombre como su nombre del almacén, o el nombre base de archivo si nombre se omite.

Cuando recursive? es verdadero, los contenidos del archivo se añaden recursivamente; si archivo designa un archivo plano y recursive? es verdadero, sus contenidos se añaden, y sus bits de permisos se mantienen.

Cuando recursive? es verdadero, llama a (select? archivo stat) por cada entrada del directorio, donde archivo es el nombre absoluto de archivo de la entrada y stat es el resultado de lstat; excluyendo las entradas para las cuales select? no devuelve verdadero.

El ejemplo siguiente añade un archivo al almacén, bajo dos nombres diferentes:

(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")

El módulo (guix packages) exporta los siguientes procedimientos monádicos relacionados con paquetes:

Procedimiento monádico: package-file paquete [archivo] [#:system (%current-system)] [#:target #f] [#:output "out"]

Devuelve como un valor monádico el nombre absoluto de archivo de archivo dentro del directorio de salida output del paquete. Cuando se omite archivo, devuelve el nombre del directorio de salida output del paquete. Cuando target es verdadero, se usa como una tripleta de compilación cruzada.

Tenga en cuenta que este procedimiento no construye paquete. Por lo tanto, el resultado puede designar o no un archivo existente. Le recomendamos que no use este procedimiento a no ser que sepa qué está haciendo.

Procedimiento monádico: package->derivation paquete [sistema]
Procedimiento monádico: package->cross-derivation paquete objetivo [sistema]

Versión monádica de package-derivation y package-cross-derivation (see Definición de paquetes).


Footnotes

(25)

Esta operación es habitualmente conocida como “bind” (asociación), pero ese nombre denota un procedimiento no relacionado en Guile. Por tanto usamos este símbolo en cierto modo críptico heredado del lenguaje Haskell.


Next: Expresiones-G, Previous: Derivaciones, Up: Interfaz programática   [Contents][Index]