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