Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
Thank you Liliana and Attila for the swift and actionable feedback :) Below is a revised proposition. Here is a minimal working example of an os declaration: --mwe.scm--- (use-modules (beaver system) (beaver functional-services) (gnu packages version-control) (gnu services web) (gnu services telephony) (gnu services ssh) (gnu services base) (guix gexp)) (-> (minimal-ovh "osef") (instantiate nginx) (instantiate mumble-server (welcome-text "coucou") (port 64738)) (extend openssh `(("alice" ,(local-file "/home/edouard/.ssh/id_rsa.pub" (modify openssh (password-authentication? #f) (allow-empty-passwords? #t)) (remove guix)) --- To see the value of this syntactic sugar, try to replicate this MWE with the standard syntax. It's not horrendous, but it *is* off-putting to many newcomers to git, whereas this sugary piece is more readable for them (sample size of 1, p=0.0005). Here is the revised functional-services.scm, not yet commited and pushed, and only lightly tested in local containers, but not in production: Advice and comments welcome :) functional-services.scm-- (define-module (beaver functional-services) #:use-module (gnu system) #:use-module (gnu services) #:export (instantiate extend modify remove)) (define syntax->string (compose symbol->string syntax->datum)) (define (service-configuration stx service) "Return the syntax one can use to refer to xxx-configuration for the given service" (datum->syntax stx (string->symbol (string-append (syntax->string service) "-configuration" (define (service-type stx service) "Return the syntax one can use to refer to xxx-service-type for the given service" (datum->syntax stx (string->symbol (string-append (syntax->string service) "-service-type" (define-syntax instantiate (lambda (stx) (syntax-case stx () [(_ os service-name) (with-syntax ([service-type (service-type stx #'service-name)]) #'(begin ((lambda (x) ;; It is wrapped in a lamba to make sure os is ;; evaluated once only. It it wasn't in a labmda, whatever ;; form os is in the calling code would be repeated ;; multiple times, and so if the form was e.g. (some-func ;; os), then some-func would be called multiple times, ;; which may not be desirable. (operating-system (inherit x) (services (cons (service service-type) (operating-system-user-services x) os)))] [(_ os service-name forms ...) (with-syntax ([service-type (service-type stx #'service-name)] [service-configuration (service-configuration stx #'service-name)]) #'(begin ((lambda (x) ;; Wrapping in a lambda for the same reasons as above (operating-system (inherit x) (services (cons (service service-type (service-configuration forms ...)) (operating-system-user-services x) os)))]))) (define-syntax extend (lambda (stx) (syntax-case stx () [(_ os service-name forms ...) (with-syntax ([service-type (service-type stx #'service-name)]) #'(begin ((lambda (x) (operating-system (inherit x) (services (cons (simple-service (format #f "A ~a extension" (syntax->string #'service-name)) service-type forms ...) (operating-system-user-services x) os)))]))) (define-syntax modify (lambda (stx) (syntax-case stx () [(_ os service-name forms ...) (with-syntax ([service-type (service-type stx #'service-name)] [service-configuration (service-configuration stx #'service-name)]) #'(begin ((lambda (x) (operating-system (inherit x) (services (modify-services (operating-system-user-services x) (service-type config => (service-configuration (inherit config) forms ...)) os)))]))) (define-syntax remove (lambda (stx) (syntax-case stx () [(_ os service-name forms ...) (with-syntax ([service-type (service-type stx #'service-name)]) #'(begin ((lambda (x)
Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
Am Sonntag, dem 26.11.2023 um 17:49 +0100 schrieb Edouard Klein: > Thank you Liliana and Attila for the swift and actionable feedback :) > > Below is a revised proposition. > > Here is a minimal working example of an os declaration: > --mwe.scm--- > (use-modules > (beaver system) > (beaver functional-services) > (gnu packages version-control) > (gnu services web) > (gnu services telephony) > (gnu services ssh) > (gnu services base) > (guix gexp)) > > (-> (minimal-ovh "osef") > (instantiate nginx) I do wish you spelled out service. Also, instantiate takes as much characters to type as add-service. > (instantiate mumble-server > (welcome-text "coucou") > (port 64738)) > (extend openssh `(("alice" ,(local-file > "/home/edouard/.ssh/id_rsa.pub" > (modify openssh > (password-authentication? #f) > (allow-empty-passwords? #t)) > (remove guix)) > --- > > To see the value of this syntactic sugar, try to replicate this MWE > with the standard syntax. It's not horrendous, but it *is* off- > putting to many newcomers to git, whereas this sugary piece is more > readable for them (sample size of 1, p=0.0005). Well, that'd be (let ((base (minimal-ovh "osef"))) (operating-system (inherit base) (services (cons* (service nginx-service-type) (service mumble-service-type (mumble-configuration (welcome-text "couocu") (port 64738))) (service openssh-service-type (openssh-configuation (password-authentication? #f) (allow-empty-passwords? #t) (authorized-keys (minimal-ovh "osef") (lambda (base) …)) On that note, we also have extend-openssh-authorized-keys for the use with modify-services. > Here is the revised functional-services.scm, not yet commited and > pushed, and only lightly tested in local containers, but not in > production: > > Advice and comments welcome :) > > > functional-services.scm-- > > > (define-module (beaver functional-services) > #:use-module (gnu system) > #:use-module (gnu services) > #:export (instantiate extend modify remove)) > > (define syntax->string (compose symbol->string syntax->datum)) > > (define (service-configuration stx service) > "Return the syntax one can use to refer to xxx-configuration for > the given > service" > (datum->syntax stx (string->symbol > (string-append > (syntax->string service) > "-configuration" > > (define (service-type stx service) > "Return the syntax one can use to refer to xxx-service-type for the > given > service" > (datum->syntax stx (string->symbol > (string-append > (syntax->string service) > "-service-type" > > (define-syntax instantiate > (lambda (stx) > (syntax-case stx () > [(_ os service-name) > (with-syntax > ([service-type (service-type stx #'service-name)]) > #'(begin > ((lambda (x) ;; It is wrapped in a lamba to make sure os > is > ;; evaluated once only. It it wasn't in a labmda, > whatever > ;; form os is in the calling code would be repeated > ;; multiple times, and so if the form was e.g. (some- > func > ;; os), then some-func would be called multiple times, > ;; which may not be desirable. Isn't it also wrapped in a lambda, because -> is a threading macro that takes functions rather than syntax? Cheers
Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
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 ...) ...))) ; 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
Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
Liliana Marie Prikler writes: >> (instantiate nginx) > I do wish you spelled out service. Also, instantiate takes as much > characters to type as add-service. > Done, see below. I was worried about the paronymy between add-service and add-services which already exists. I defer to you and your experience on this, naming things is hard. >> To see the value of this syntactic sugar, try to replicate this MWE >> with the standard syntax. It's not horrendous, but it *is* off- >> putting to many newcomers to git, whereas this sugary piece is more >> readable for them (sample size of 1, p=0.0005). > Well, that'd be > ... I tried: (let ((base (minimal-ovh "osef"))) (operating-system (inherit base) (services (cons* (service nginx-service-type) (service mumble-server-service-type (mumble-server-configuration (welcome-text "couocu") (port 64738))) (service openssh-service-type (openssh-configuration (password-authentication? #f) (allow-empty-passwords? #t) (authorized-keys `(("alice" ,(local-file "/home/edouard/.ssh/id_rsa.pub")) (operating-system-user-services base) I admit that this is as readable as the sweet version, but: - Openssh is already defined in (minimal-ovh "osef"), so the build fails with: 'guix system: error: service 'ssh-daemon' provided more than once' - you forgot the removal of guix-service, which admitedly is not used much in practice anyway The problem with those two is that they break the nice structure of just adding services, and now one has to first remove an element from (operating-system-user-services base), then edit one of the element of the resulting list, then add to elements to it. It is probably possible to write it in a readable way, maybe less so than the sweet version, but not so much as to justify adding the new macros to guix. However the readability is not the main selling point, the writeability is. Please do not discount how hard it is to write that kind of stuff when scheme's not your main language. It is possible people here forgot how hard this is for beginners, especially those like me whose brain is deformed from years using algol-derived syntaxes. I think we are losing a lot of mindshare because of the hard step of learning both guile and guix, and the kind of syntactic sugar I suggest may help bridge the gap long enough for newcomers to ease into the syntax, after they get a taste of guix' capabilities. Another experiment: with the sweet syntax, you can easily extend services, without having to define a -record-type, etc. Just define a function. I can write more at length about that if you want. > On that note, we also have extend-openssh-authorized-keys for the use > with modify-services. > I see it now in the source, but I was unaware of its existence. Thanks for the swift again review :) Cheers, Edouard. -mwe.scm (use-modules (beaver system) (beaver functional-services) (gnu packages version-control) (gnu services web) (gnu services telephony) (gnu services ssh) (gnu services base) (guix gexp)) (-> (minimal-ovh "osef") (add-service nginx) (add-service mumble-server (welcome-text "coucou") (port 64738)) (extend-service openssh `(("alice" ,(local-file "/home/edouard/.ssh/id_rsa.pub" (modify-service openssh (password-authentication? #f) (allow-empty-passwords? #t)) (remove-service guix)) --functional-services.scm (define-module (beaver functional-services) #:use-module (gnu system) #:use-module (gnu services) #:export (add-service extend-service modify-service remove-service)) (define syntax->string (compose symbol->string syntax->datum)) (define (service-configuration stx service) "Return the syntax one can use to refer to xxx-configuration for the given service" (datum->syntax stx (string->symbol (string-append (syntax->string service) "-configuration" (define (service-type stx service) "Return the syntax one can use to refer to xxx-service-type for the given service" (datum->syntax stx (string->symbol (string-append (syntax->string service) "-service-type" (define-syntax add-service (lambda (stx) (syntax-case stx () [(_ os service-name) (with-syntax ([service-type (service-type stx #'service-name)]) #'(begin ((lambda (x) ;; It is wrapped in a lamba to make sure os is ;; evaluated once only. It it wasn't in a labmda, whatever ;; form os is in the calling code would be repeated ;; multiple times, and so if the form was e.g. (some-func ;; os), then some-fu
role of core-updates
Hi, hope Guix maintainers can clarify the role of the now core-updates branch; the current documentation does not specify the core-updates branch as a thing but there are clearly interests and uses of this branch for package updates not belonging to a feature branch like gnome and it is useful for, say, updating to the GNU make package which would have caused world rebuild. Thanks