Re: Idea: Function composition to declare operating-system
Hi Théo, Théo Maxime Tyburn skribis: > 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. Neat! In some cases, having “transformation” functions is clearer indeed. There’s a couple of them in the code, such as ‘virtualized-operating-system’, that make it easy to adapt an OS config to a specific use case. Thanks for sharing! Ludo’.
Re: Idea: Function composition to declare operating-system
Am 29.08.22 um 17:14 schrieb Théo Maxime Tyburn: 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? I really like the idea! -- Regards Hartmut Goebel | Hartmut Goebel | h.goe...@crazy-compilers.com | | www.crazy-compilers.com | compilers which you thought are impossible |
Re: Idea: Function composition to declare operating-system
Hi muradm! muradm writes: [...] >> --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 > > To clarify, do you ask that in the end of the day, some where > in (gnu services ...) there should be exported `add-jackd` > function? If so, I beleive that this will increase cross > dependency between things, thus decreasing flexibility. > > Imagine `add-jackd` maintainer should always keep track on > what is being added into guix, that potentially may cause > conflict with jackd and/or require adjustments in `add-jackd` > function implementation. > > Also, IMHO, implementation of `add-jackd` would be very > much opinionated. Actually I was thinking that the functions that build up the system like `add-jackd` would be written by users. It is still user configuration, just packaged in a function. So no one would "have to" maintain it. But this could still provide an easy way to share independent parts of a system configs. Especially for features requiring a complex setup, a canonical configuration could be something you would want to maintain. But it would be up to the maintainer. Assuming tough, someone wants to maintain `add-jackd` under (gnu services ...), it should not be more maintenance effort than the effort each and every user of jackd has to put so that it’s system configuration doesn’t break. So I don’t see anything against it. But I might be wrong. >> >> 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: >> >> ((apply compose (reverse os-functions)) minimal-os) >> > > [...] > >> >> (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)) >> > > I suppose this could be useful to have it in guix toolbox, > although I would prefer to have (required '(account activate ...)) > or (required %fixed-system-service-types) optional argument, > instead of refering to global constant %fixed-system-service-types, > which might not satisfy everyone requirements. I agree, better make this optional. If anything, only this this kind of helper function would be added to guix so that users can define their system conveniently using this function composition approach. >> and also force keeping or dropping of some services if needed. The >> list >> of services that gets duplicated seems to be this one: >> >> (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)) >> >> 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: >> >> (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 >>i
Re: Idea: Function composition to declare operating-system
Hi, I had similar problem popping up periodically. So here are my 10 cents.. Théo Maxime Tyburn writes: Hi guix! [...] --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 To clarify, do you ask that in the end of the day, some where in (gnu services ...) there should be exported `add-jackd` function? If so, I beleive that this will increase cross dependency between things, thus decreasing flexibility. Imagine `add-jackd` maintainer should always keep track on what is being added into guix, that potentially may cause conflict with jackd and/or require adjustments in `add-jackd` function implementation. Also, IMHO, implementation of `add-jackd` would be very much opinionated. 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: ((apply compose (reverse os-functions)) minimal-os) [...] (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)) I suppose this could be useful to have it in guix toolbox, although I would prefer to have (required '(account activate ...)) or (required %fixed-system-service-types) optional argument, instead of refering to global constant %fixed-system-service-types, which might not satisfy everyone requirements. and also force keeping or dropping of some services if needed. The list of services that gets duplicated seems to be this one: (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)) 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: (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) 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? Did you try using (modify-services ...)? Basically, you can achieve similar goal within services list only with it. Anyway that was a very fun hacking session :) Happy Hacking! Théo Thanks in advance, muradm
Idea: Function composition to declare operating-system
Very cool!
Idea: Function composition to declare operating-system
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) (t