Siguiente: , Anterior: , Subir: Interfaz programática   [Índice general][Índice]


8.9 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 (véase 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

Fíjese que el módulo (guix monad-repl) extiende la sesión interactiva de Guile con nuevas “meta-órdenes” para facilitar el trabajo con procedimientos monádicos: run-in-store y enter-store-monad. El primero se usa para “ejecutar” un valor monádico único a través del almacén:

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.

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.

Sintaxis Scheme: with-monad mónada cuerpo ...

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

Sintaxis Scheme: return val

Devuelve el valor monádico que encapsula val.

Sintaxis Scheme: >>= mval mproc ...

Asocia el valor monádico mval, pasando su “contenido” a los procedimientos monádicos mproc19. 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
Sintaxis Scheme: mlet mónada ((var mval) ...) cuerpo ...
Sintaxis Scheme: 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 (véase Local Bindings en GNU Guile Reference Manual).

Sistema Scheme: mbegin mónada 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.

Sistema Scheme: mwhen condición 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.

Sistema Scheme: munless condición 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 Scheme: %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 Scheme: 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 Scheme: %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 Scheme: 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 (véase Definición de paquetes).


Notas al pie

(19)

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.


Siguiente: , Anterior: , Subir: Interfaz programática   [Índice general][Índice]