Hi Sébastien,

Sébastien Gendre <[email protected]> writes:

Hello everyone,

I was trying to understand how to use `define-configuration` with some
practice and couldn't understand what goes wrong.

It took for example to write a `luanti-game-configuration` that could, when serialized, generate a configuration file for the Luanti server. I
only started with 1 parameter.

When I try to serialize a configuration instance to a file, the content
of this file is empty.

Here is the code defining my `luanti-game-configuration`:


    ;; -*- mode: scheme; -*-
    ;; Luanti game configuration

    (use-modules (gnu services configuration)
                 (guix gexp))

    ;; Define the maybe string
    (define-maybe string)

Note that this expects a procedure named `serialize-string', which causes a warning because it doesn’t exist.


    ;; Define serializer functions
    (define (serialize-luanti-admin-name field-name value)
"Serialize the Luanti admin name field for the config file."
      (let ((field-real-name "name"))
        #~(string-append #$field-real-name " = " #$value)))

Since you’re only building strings, you don’t need a gexp, nor do you need the let binding. Here’s a simplified version:

   (define (serialize-luanti-admin-name _ value)
     "Serialize the Luanti admin name field for the config file."
     (string-append "name = " #$value))


    ;; Define the Luanti Game configuration
    (define-configuration luanti-game-configuration
      (admin-name
       maybe-string
       "Username of the administrator."
       (serializer serialize-luanti-admin-name)))

This is mixing an optional predicate with a required serializer, which won’t work right. While it’s boilerplatey, I find that the best approach is to make one type per field:

   (define admin-name? string?)
(define (serialize-admin-name field-name value) "do the right thing here")

Then use `define-maybe' and the predicate & serializer it expands to:

   (define-maybe admin-name)

   (define-configuration luanti-game-configuration
(admin-name maybe-admin-name "Username of the administrator."))

Within the configuration system, "type" covers both the value *and* how it’s handled, which can cause problems when you have two fields which take string, but need different serialize-string handling -- like one needing to be quoted, or one representing an INI-style [section] and another a key = value pair. Starting here gives you clean separation you can merge later, if you like.


    ;; Define Luanti game configuration file generator
    (define (minetest.conf config)
      "Return the Luanti config file."
      (plain-file "minetest.conf"
       (with-output-to-string
         (lambda ()
           (serialize-configuration
            config
            luanti-game-configuration-fields)))))

This is the crux of your problem. The `with-output-to-string' procedure captures any value *printed* and returns it as a string. Your lambda doesn’t print anything, because `serialize-configuration' doesn’t print anything, it returns a value. Since there’s no output, `with-output-to-string' returns an empty string, and `plain-file' puts that in your minetest.conf. Whoops.

The procedure should be:

   ;; Define Luanti game configuration file generator
   (define (minetest.conf config)
     "Return the Luanti config file."
     (mixed-text-file
      "minetest.conf"
(serialize-configuration config luanti-game-configuration-fields)))

Note that if your serializers use gexps, you will need to build the mixed-text-file to see anything. If you’ve used the (guix) module in your Guile REPL, you can do this with:

   ,build (minetest.conf ...)

On a closing note, I’ll share that I’ve also struggled a lot with the configuration system in Guix, both in its documentation and implementation. Implementation-wise, the system feels both barebones and verbose. I’ve run into some stuff with unsatisfying expressions, like fields whose names aren’t needed, config subsections, and configurations spanning multiple files. These all require workarounds which feel more like subverting the system than using it as intended.

On the documentation side, the Guix manual is an invaluable reference, but a lot of it feels like just that -- a reference. It has great "what" and "how" information, but in my opinion, sorely lacks the contextual "why" and "when" to effectively apply it.

 -- Ian

Reply via email to