unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Théo Maxime Tyburn" <theo.tyburn@gmail.com>
To: guix-devel@gnu.org
Subject: Idea: Function composition to declare operating-system
Date: Mon, 29 Aug 2022 17:14:11 +0200	[thread overview]
Message-ID: <87edwylxe9.fsf@gmail.com> (raw)

Hi guix!

I experimented on a functional approach to the operating
system declaration. My goal was to be able to pass an operating-system
instance to a composition of functions, modifying it one after another
and returning the desired operating-system object. I find this approach
more convenient because it allows for better segmentation of the system
features. I achieved to make it work for the system declaration I
usually use and I’d like to share it with you.

--BEGIN USE_CASE
For example to add jackd to my system I need to add the "realtime"
group, add some users to this group and add a pam-limits-service. If I
want to remove this functionality from my system using the declarative
approach I have to look down my config file for places where I added
these things. Usually I partially solve this problem by putting comments
to signal the purpose of each code block in the system declaration.

But wouldn’t it be better if I just had a function `add-jackd` that takes an
operating-system instance and returns the os with the extra functionalities ?
--END USE_CASE

So that was the purpose of the experimentation. It didn’t turn out to be
too complicated to implement. At least for my use case, I just needed to add two helper
functions to extend users and services fields. The rest is handled directly by
record inheritance and by accessing the fields of the input operating-system.

The final declaration looks like this:
--8<---------------cut here---------------start------------->8---
((apply compose (reverse os-functions)) minimal-os)
--8<---------------cut here---------------end--------------->8---


Where `minimal-os` is roughly an (operation-system) declaration with at
least all the required fields. `os-functions` is a list of procedures
taking an operating-system as input and returning an operating-system.

An example for one such function that adds a label:
--8<---------------cut here---------------start------------->8---
(define (label-os os)
  (operating-system
   (inherit os)
   (label (string-append (operating-system-host-name os) ":"
						 "refactor-functional-os" ;; my custom tag
						 " " (operating-system-default-label this-operating-system)))))
--8<---------------cut here---------------end--------------->8---

The most problematic thing is handling services. For some reason I
ignore, there is a set of services that always gets added
again to the list of services when you call (operating-system ...) with a
services field with more than one service. So you can’t just do like
above and
--8<---------------cut here---------------start------------->8---
(define (add-some-service os)
  (operating-system
   (inherit os)
   (services (cons (simple-service ...)
				   (operating-system-services os)))))
--8<---------------cut here---------------end--------------->8---
because some services that need to be unique would come more than once
in the final operating-system object.

So I just added a simple function to filter them out
--8<---------------cut here---------------start------------->8---
(define* (extend-operating-system-services os services #:key (drop '()) (keep '()))
  (append (filter (lambda (service)
					(not (member (service-type-name (service-kind service))
								 (filter (lambda (s) (not (member s keep)))
										 (append drop %fixed-system-service-types)))))
				  (operating-system-services os))
		  services))
--8<---------------cut here---------------end--------------->8---
and also force keeping or dropping of some services if needed. The list
of services that gets duplicated seems to be this one:
--8<---------------cut here---------------start------------->8---
(define %fixed-system-service-types
  '(account activate boot cleanup etc file-systems firmware fstab guix host-name linux-bare-metal linux-builder pam profile root-file-system session-environment setuid-program shepherd-root system user-processes))
--8<---------------cut here---------------end--------------->8---
I generated the list by just checking which services get duplicated, so I am not
very sure about it. There surely is a better way to get it. 

Anyway I can now define a function adding desktop functionalities:
--8<---------------cut here---------------start------------->8---
(define (x-os os)
  (operating-system
   (inherit os)
   (services
	(extend-operating-system-services
	 os
	 (list
	  ;; slim display manager
	  (service slim-service-type
			   (slim-configuration
				(display ":0")
				(vt "vt7")
				(theme %default-slim-theme)
				(theme-name %default-slim-theme-name)
				(xorg-configuration
				 (xorg-configuration
				  (keyboard-layout (operating-system-keyboard-layout os)))))))
	 #:drop '(gdm)))
   (packages (cons*
			  ;; window managers
			  i3-wm python-py3status
			  emacs-nc-exwm-xdg
			  (operating-system-packages os)
			  ))))
--8<---------------cut here---------------end--------------->8---

Of course there is room for some macros to make this more elegant, but
this is the rough idea.

In a way it feels like treating the operating-system like a service
you can extend. Maybe it would even make sense to implement this as a
service ? Not sure about that.

It seems it would also be reasonable to have something like an
operating-system-configuration record and a way to compose some before
putting them into an operating-system record (it seems to be the
approach rde `features` are based on). But I felt too lazy to copy all the
fields from the operating-system record definition. There might be a
way to get all the fields programatically and define a
record/configuration though.

Anyway, what do you think about this functionality? Have you already experimented with similar things?
Did I reinvent the wheel? Is there a better approach?

Anyway that was a very fun hacking session :)

Happy Hacking!

Théo


             reply	other threads:[~2022-08-29 18:26 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-29 15:14 Théo Maxime Tyburn [this message]
2022-08-30  8:49 ` Idea: Function composition to declare operating-system muradm
2022-08-30 10:52   ` Théo Maxime Tyburn
2022-08-31 13:08 ` Hartmut Goebel
2022-09-02 14:11 ` Ludovic Courtès
  -- strict thread matches above, loose matches on Subject: below --
2022-08-29 20:52 Nathan Dehnel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87edwylxe9.fsf@gmail.com \
    --to=theo.tyburn@gmail.com \
    --cc=guix-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).