Hello guix,
I'll chime in because I've been playing around with this kind of thing
in my channel [1]
for a bit now and perhaps some of the ideas will be deemed useful.
(service+ OS SERVICE [CONF])
(service- OS SERVICE)
(modify-service OS SERVICE UPDATE)
I found that defining a functional API like this is about as convenient
as the original version, more examples: [2].
Another small change I tried was to not have -> be a macro but rather a
simple fold.
I took what the dot-macro form expands to and made it a curried function
called services
(but remove and modify could be equivalently defined),
which takes a service and then returns a lambda that takes an OS and
modifies it
just as the beaver-labs macros do with that extra service.
(define ((services . new) os)
(operating-system
(inherit os)
(user-services
(append new (operating-system-user-services os)))))
(define (-> system . services)
(fold (cut <> <>) system services))
(-> os (services (service openssh-service-type)) ...) ; yields os with
added openssh
((services (service openssh-service-type)) os) ; equivalent
The one macro that is indispensable for terse configurations is still
some sort of service+ (I use &s as the name),
which appends -service-type, surrounds the body in a -configuration [3],
and in my case calls `services` on it for good measure to get the os
modifying lambda straight from the service+ clause.
(-> os
(&s openssh)
(&s guix-publish
(advertise? #t))
...)
This ends up save for 2 extra characters being syntactically almost
identical to the original version,
just passes around lambdas instead of getting manipulated by nested macros.
Them being functions they're now easier to work with with in more
traditional Scheme ways,
for example compose works, and we can wrap them in other functions that
for example apply them only in some circumstances.
Or combine transformations into new ones. The definitions for which end
up being remarkably short and generalizable.
(-> os
(compose (os/hostname "the-dam.org") (&s gpm)) ; freely composable
with other functions of the same sort
(%services-with-arguments ...) ; indifferent to being composed on the
spot even in the middle of threading
(if-host "hydra" (&s openssh)) ; only adds openssh if the host is
named hydra, simple function
; since the second argument is just a function that may or
may not be applied by the new lambda
((mapped-file-systems <some-devices> ...)
<more-devices-that-depend-on-former> ...))) ; not threading by
macro allows more complex structures without confusion
Then again none of this has had proper field testing, the fact that it
has less gotchas is just a personal opinion.
Cheers, and all the best
[1]:
https://git.sr.ht/~michal_atlas/guix-channel/tree/a31b68b46da60002383e2793eba88b99fc5c2382/item/atlas/combinators.scm
(modified from the original beaver-labs version).
[2]:
https://git.sr.ht/~michal_atlas/dotfiles/tree/8c78f53139ae176ff0a4cab82ad0fb64bce6989b/item/atlas/config/system/services.scm#L56
[3]:
https://git.sr.ht/~michal_atlas/guix-channel/tree/master/item/atlas/utils/services.scm#L53