guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Syntactic Diabetes (was Re: A friendlier API for operating-system de


From: Michal Atlas
Subject: Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
Date: Sun, 26 Nov 2023 05:36:00 +0000

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]