guix-devel
[Top][All Lists]
Advanced

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

A new paradigm for modifying operating system declarations


From: raid5atemyhomework
Subject: A new paradigm for modifying operating system declarations
Date: Mon, 04 Jan 2021 15:38:38 +0000

Hi guix-developers,

I'd like to propose an idea for constructing `<operating-system>` objects.

First, let me present the `decorate` form:

```scheme
(define-syntax decorate
  (syntax-rules ()
    ((decorate ((x ...)) a ...)
     (x ... a ...))
    ((decorate (x) a ...)
     (x a ...))
    ((decorate ((x ...) y ...) a ...)
     (x ... (decorate (y ...) a ...)))
    ((decorate (x y ...) a ...)
     (x (decorate (y ...) a ...)))))
```

Here's an example usage, instead of:

```scheme
(with-output-to-port (current-error-port)
  (lambda ()
    (system "echo" "an error occurred")))
```

We can use:

```scheme
(decorate ((with-output-to-port (current-error-port))
           (lambda ()))
  (system "echo" "an error occurred"))
```

The reason why this is relevant, is that when I was tying out 
https://issues.guix.gnu.org/45643 , I ended up having several changes to the 
base `operating-system` form:

```scheme
(define system-zfs (make-zfs-package linux-libre-5.4))
(operating-system
  ; ... other fields ...
  (kernel linux-libre-5.4)
  (kernel-loadable-modules (list (list system-zfs "module")))
  (packages (cons* system-zfs
                   ; ... other packages ...
                   %base-packages))
  (services (cons* (service zfs-loader-service-type system-zfs)
                   ; ... other services ...
                   %desktop-services)))
```

So, I imagined instead of exposing `make-zfs-package` and 
`zfs-loader-service-type` and requiring so many modifications to various fields 
of `operating-system` form, expose instead a `install-zfs` form, like so:

```scheme
(install-zfs
  (operating-system
    (kernel linux-libre-5.4)
    ; ... other fields ...
    ))
```

This `install-zfs` form would be defined as below:

```scheme
(define-public (install-zfs os)
  (define system-zfs (make-zfs-package (operating-system-kernel os)))
  (operating-system
    (inherit os)
    (location (operating-system-location os))
    (kernel-loadable-modules (cons (list system-zfs "module")
                                   (operating-system-kernel-loadable-modules 
os)))
    (packages (cons system-zfs
                    (operating-system-packages os)))
    (services (cons (sevice zfs-loader-service-type system-zfs)
                    (operating-system-services os)))))
```

This would install ZFS "correctly", by adding the module to kernel loadable 
modules, adding the package so that ZFS can be managed at runtime, and adding 
the service to load ZFS module and import ZFS pools.  The hope is that this 
reduces the scope for errors in defining the operating system.

On the other hand, if this kind of pattern becomes common, then consider:

```scheme
(install-foo
  (install-bar
    (install-zfs
       (operating-system
          #;...))))
```

Which brings us back to the `decorate` form, which reduces nestedness:

```scheme
(decorate (install-foo
           install-bar
           install-zfs
           operating-system)
   #;...)
```

This seems quite elegant to me.

Now for example we can consider that the `"zfs"` module supports various 
options as well, which would be put in a `/etc/modprobe.d/zfs.conf` file. We 
could consider for example that `install-zfs` could support an `options` keyed 
argument, which it will then add to the `modprobe` configuration file in an 
`etc-service-type` in a new service that it extends to the given 
`<operating-system>`.

```scheme
(define install-zfs-full
  (lambda* (#:key (options '()) os)
    #;...))
(define-public install-zfs
  (match-lambda
    ((os)
     (install-zfs-full #:os os))
    (rest
     (apply install-zfs-full rest))))
```

Then in a system configuration:

```scheme
(decorate ((install-zfs #:options '(("zfs_arc_max" 5000000000)) #:os)
           operating-system)
  #;...)
```

Something similar could be done for the `ddcci-driver-linux` example in the 
documentation for `kernel-module-loader-service-type`:

```scheme
(define-public install-ddcci
  (match-lambda
    ((os)
     (install-ddcci-full #:os os))
    (rest
     (apply install-ddcci-full rest))))
(define install-ddcci-full
  (lambda* (#:key (options '())) os)
    (define config-file
      (plain-file "ddcci.conf"
        (if (null? options)
            ""
            (string-join (cons "options ddcci" options)
                         " "))))
    (operating-system
      (inherit os)
      (location (operating-system-location os))
      (kernel-loadable-modules (cons ddcci-driver-linux
                                     (operating-system-kernel-loadable-modules 
os)))
      (services
        (cons* (service kernel-module-loader-service-type
                 '("ddcci" "ddcci_backlight"))
               (simple-service 'ddcci-config etc-service-type
                 `(("modprobe.d/ddcci.conf" ,config-file)))
               (operating-system-services os))))))
```

Then, in the system configuration file:

```scheme
(decorate (install-zfs
           (install-ddcci #:options '("dyndbg" "delay=120") #:os)
           operating-system)
   (name "example-system")
   #;...)
```

Obviously, it's called "decorate" since it's partly inspired by Python 
decorators, which use a similar-looking pattern, with different syntax.

What are your opinions?  Blech?  Yummy?  Is it worth exploring this paradigm 
for adding particularly complex features to an operating system definition?

Thanks
raid5atemyhomework



reply via email to

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