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: Edouard Klein
Subject: Re: Syntactic Diabetes (was Re: A friendlier API for operating-system declarations)
Date: Sun, 26 Nov 2023 21:46:19 +0100
User-agent: mu4e 1.10.2; emacs 28.2

Liliana Marie Prikler <liliana.prikler@gmail.com> 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.00000005).
> 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-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-service
  (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-service
  (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-service
  (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
                  (modify-services (operating-system-user-services x)
                    (delete service-type)))))
             os)))])))



reply via email to

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