[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: A friendlier API for operating-system declarations
From: |
antlers |
Subject: |
Re: A friendlier API for operating-system declarations |
Date: |
Mon, 19 Feb 2024 14:25:51 -0800 |
User-agent: |
Cyrus-JMAP/3.11.0-alpha0-144-ge5821d614e-fm-20240125.002-ge5821d61 |
Hi!
Just wanted to say that I really admire your take on end-user service
configuration in the Beaver Labs channel.
I gravitated towards composing functions over `operating-systems` myself,
though my config is probably only ""notable"" for the moderately-cursed
`modify-record` macro that I use to derive/configure/wrap the services[1]:
```
(define (os-with-yubi parent users*)
(modify-record parent
(groups -> (cons (user-group (name "plugdev")) <>))
(users -> (map (lambda (user)
(if (member (user-account-name user)
users*)
(modify-record user
(supplementary-groups -> (cons "plugdev" <>)))
user))
<>))
(services => (append <> (list
(service pcscd-service-type)
(simple-service 'u2f-udev-rules udev-service-type
(list (specification->package "libu2f-host")))
(simple-service 'yubi-udev-rules udev-service-type
(list (specification->package
"yubikey-personalization"))))))))
```
It's like if `modify-services` was generalized over any kind of record, but
instead of using pre-defined verbs like `remove`, the `body` component of each
`(field-name -> body)` clause is wrapped in an implicit SRFI-26 `cut`-like[2]
form to create an anonymous function that's applied to the field's value. It's
a super leaky abstraction because I use a different symbol for `->` depending
on whether that record-field is a plain value, a thunk, or a `delay`-ed form
(and it could be implemented more efficiently), but it greatly reduces the
length and indentation level of repeatedly nested, inherited record variations.
```
((compose os-with-yubi
[...])
[operative-system])
```
I only wrote a handful of top-level `operating-system transformation`
functions, and IIRC I only composed them at that top level; I think the way
that you've broken them up into smaller forms and composed them out of each
other though deeper, standard-functional composition gives you that same
additive benefit over would-be nested forms, with each definition roughly
matching up to one my "anonymous" invocations.
What I still dwell on is whether there's a way to further minimize the
code-volume of including additional functionality (as was pondered in a prior
response, and as `modify-record` does in obsoleting `modify-services`'s verbs),
and how best to avoid order-dependencies and expose inherit
inter-service-configuration dependencies and conflicts. Tropin's RDE uses an
emacs or systemd-esque `provides`/`requires` system which is satisfying, but
introduces implicit standardization on the symbols associated with software
roles and builds a significant graph of them, which I feel adds to the "bulk"
of the (still very elegant) solution and embraces the need to wrap every
service and transformation into their cohesive system-- that's part of what's
kept me on plain Guix with my bandaid-solutions (in the spirit of learning the
standard approach before exploring larger systems built on top of it).
Anyway, I like your take, just fount it today and got to thinking-- thanks for
putting it out there~
1: From:
https://github.com/AutumnalAntlers/old-guix-config/blob/main/modules/antlers/systems/transformations/yubi.scm
2: Like `cut`, but deeper: see the `<>` symbol nested deep within in the
`users` clause of the Yubi example.