Subject: [bug#52600] [PATCH] doc: Document (gnu services configuration).

From: Xinglu Chen <[email-masked]>
Date: Thu, 23 Dec 2021 13:54:44 +0100

Attila schrieb am Donnerstag der 23. Dezember 2021 um 11:18 GMT:

>> Great, thanks for taking a look. I didn’t receive any message from
>> Attila though, and there doesn’t seem to be anything on the ML either.
>>
>> I guess he sent it when all the GNU infra was down, but unless he didn’t
>> Cc me, I don’t see why I wouldn’t receive it.
>
> yep. since then i have resent it, available at:
>
> https://issues.guix.gnu.org/52600#1

Oh, I already had it in my archive, but it was missing a subject, and it
wasn’t part of any thread, so I didn’t see it.

Now to the reply:

> as for some higher level feedback:
> 
> i have just finished my first Guix service, a rather complex
> one. based on the examples, and on the config codebase itself, i had
> used define-configuration, and i had encountered a surprise WRT
> undefined values and maybe- types (only after that have i found this
> documentation).
> 
> > default-value is the default value corresponding to the field; if
> > none is specified, the user is forced to provide a value when creating
> > an object of the record type.
> 
> i was expecting it to be possible to have a field like:
> 
> (foo
> (maybe-integer?))

A ‘maybe-’ type doesn’t necessarily have to have a default value set to
‘disabled’.  The default value of the ‘foo’ field could just as well be
‘3’ or something.

> and its behavior would be to hold an undocumented value by default,
> that the service implementations need to check for using a public
> predicate function.

What do you mean by “undocumented value”?

> some of the config values in my service can conditionally derive its
> default value based on the value of other fields.

I don’t think this is possible with ‘define-configuration’ yet.  But it
would be a nice feature to have.

> i need to be able to differentiate between undefined or user provided
> field values (i.e. completely independent of anything related to
> serialization).

Maybe we could change ‘undefined’ to instead be an exception, which will
raised when the user doesn’t provide anything.

> > Sometimes a field should not be serialized if the user doesn’t specify a
> > value. To achieve this, you can use the @code{define-maybe} macro to
> > define a ``maybe type''; if the value of a maybe type is set to the
> > @code{disabled}, it will not be serialized.
> 
> the use of 'disabled here was very confusing because configuration
> objects are typically full of boolean fields... is 'disabled a valid
> app value, or part of the guix API?

Boolean fields would be specified using Guile booleans, which would then
get serialized to whatever syntax the configuration language expects.
But you are right that it could be ambigous sometimes.

> maybe we should use guile's *undefined*, and undefined? predicate
> (which is sadly a macro). or reexport an undefined? function, and
> shadow guile's macro? it's messy, and guile specific.

I am not familiar with Guile internals, but I think that
‘#<unspecified>’ is just a thing that the pretty-printer prints.  Maybe
we could use “proper” Maybe types, like in SRFI-189[1]?

[1]: Using <https://srfi.schemers.org/srfi-189/srfi-189.html>

> or maybe we could use a heap object of an unusual/private type in a
> global private variable to represent undefined field values, and add a
> public predicate to test for it.  using a cons cell for this is
> tempting, but it would leak implementation details for fields of type
> cons?. i'm new to scheme, but the best candidate is maybe a private
> dummy record instance?

But what if a user wants to set a field to ‘disabled’ (because they
don’t want anything to get serialized), then that record would have to
be public.

> i'd add a configuration-undefined-value? predicate, and also add a
> configuration-defined-value? whose semantics is to return the value
> itself, or #false when undefined. it comes handy in (or
> (defined-value? foo) 42) patterns for non-boolean fields.

But what if the value itself is #f?  You wouldn’t be able to distinguish
between the cases where the value is undefined and when the value is #f.

> in fact, i had these two in my service impl, before reading/writing
> any of this:
> 
> (define (defined-value? x)
> (if (eq? x 'undefined) #false x))
> 
> (define (undefined-value? x)
> (eq? x 'undefined))
> 
> then the question arises: do we want to differentiate between the
> cases when the field value comes from a default form, and when it is
> set by the user (e.g. at object construction time)? if so, then one
> well-known value as a marker is not enough, but i don't think it's
> worth the additional complexity. people with rare, complex use-cases
> can always resort to define-record*.

I don’t really see a use of having that functionality; do you have any
examples when this would be useful to have?

> another thing that has initially misled me was the word 'serialize': i
> don't have a better suggestion, but i have associated it to a more
> formal serialize/deserialize protocol, as opposed to turning scheme
> objects into various different configuration file formats that are
> native for the target binary.
> 
> maybe it's worth hinting at in the documentation where serialization
> is first mentioned.

The second paragraph explains this, no?  Or do you think it can be
improved?

--8<---------------cut here---------------start------------->8---
The main utility is the @code{define-configuration} macro, which you
will use to define a Scheme record type (@pxref{Record Overview,,,
guile, GNU Guile Reference Manual}).  The Scheme record will be
serialized to a configuration file by using @dfn{serializers}, which are
procedures that take some kind of Scheme value and returns a
G-expression (@pxref{G-Expressions}), which should, once serialized to
the disk, return a string.  More details are listed below.
--8<---------------cut here---------------end--------------->8---

> if the API of validate-configuration is to raise errors, then maybe it
> could return the config object if everything is fine. that can
> simplify the code at the call site.

That’s probably a good idea.