Hey guix! I'm trying to flush out the (guix records) for the =opensmtpd-service=. It's super fun doing it by the way. If you've never messed with (guix records) then you should totally give it a try. Thanks for the author of (guix records). You did a really great job! The purpose of this email is to get some suggestions on my opensmtpd-records.scm file and to use this email as a VERY ROUGH draft for a (guix records) blog post, if there is interest in it. :) The code that I have so far can be seen here: https://notabug.org/jbranso/linode-guix-system-configuration/src/master/opensmtpd-records.scm I'm open to suggestions. :) I have a basic procedure that turns a <opensmtpd-configuration> into a string... (define (opensmtpd-configuration->string record) ...) I've had a ton of fun using the =(thunked)= bit in records. The documentation in the source code says something like this (also does the guix manual document guix records somewhere?). #+BEGIN_EXAMPLE The 'port' field is \"thunked\", meaning that calls like '(thing-port x)' will actually compute the field's value in the current dynamic extent, which is useful when referring to fluids in a field's value. Furthermore, that thunk can access the record it belongs to via the 'this-thing' identifier. #+END_EXAMPLE I've found some "too-clever" ways of using (thunked) that people on irc mentioned may NOT be wise to use. For example, using (thunked) to define mutually exclusive fieldnames and a fieldname that returns another fieldnames value. I think these are not "good" ways of using (thunked). Anyone have any good ideas for using (thunked)? Below is some obligatory code. * This bit of guix records shows off the =(thunked)= and =this-record=. =(thunked)= lets you refer to the record that is being defined. In this case, I am defining mutually exclusive fieldnames...If =relay= is defined, then =local-delivery= returns =#f= as its value, which is pretty awesome. *Note*: a simpler way to define a mutually exclusive fieldnames is to have one fieldname that accepts =<cat-record>= or =<dog-record>=. #+BEGIN_SRC scheme (define-record-type* <opensmtpd-action> opensmtpd-action make-opensmtpd-action opensmtpd-action? this-record (name opensmtpd-action-name (default "local")) (relay opensmtpd-action-relay (default "#f") (local-delivery opensmtpd-action-local-delivery ;;type <opensmtpd-local-delivery-configuration> ;; local-delivery has a default value so (service opensmtpd-service) will just work for ;; local email delivery (default (opensmtpd-local-delivery-configuration)) (thunked) (sanitize (lambda (value) (cond ;; if relay has a value, then local-delivery should be #f ;; man (guix records) rocks! ;; TODO/FIXME, perhaps I should merge fieldnames 'relay', 'local-delivery', 'forward-only', ;; and 'expand-only' into one fildname called "method". Then the user has to use a specific ;; record type to be mutually exclusive. [(opensmtpd-action-relay this-record) #f] [(opensmtpd-local-delivery-configuration? value) value] [else (display (string-append "<opensmtpd-action> fieldname 'local-delivery' " "needs to be of type <opensmtpd-local-delivery-configuration>.\n")) (throw 'bad! value)]))))) #+END_SRC * This bit of guix records defines a fieldname which returns the type of another fieldname Fieldname =type= returns the type of values that =values= has either ='list-of-strings= or ='assoc-list=. *Note*: It is probably better to define a function outside of the record that returns the type that value is. That just makes it conceptually easier for the next hacker. #+BEGIN_SRC scheme (define-record-type* <opensmtpd-table> opensmtpd-table make-opensmtpd-table opensmtpd-table? this-record ;; string ;; is a list of values or key values ;; eg: (list "mysite.me" "your-site.com") ;; eg: (list ("joshua" . "joshua@gnu.org") ("james" . "james@gnu.org")) (values opensmtpd-table-values (default #f) (sanitize (lambda (value) (if (or (list-of-strings? value) (assoc-list? value)) value (begin (display "<opensmtpd-table> fieldname: 'value' must be a list of strings or an association list.\n") (throw 'bad! value)))))) ;; can be of type: (quote list-of-strings) or (quote assoc-list) ;; type is discovered during the initialition phase. The user SHOULD NEVER set the type. ;; TODO jpoiret: on irc reccomends that I just use an outside function to determine fieldname 'values', type. ;; it would be "simpler" and possibly easier for the next person working on this code to understand what is happening. (type opensmtpd-table-type (default #f) (thunked) (sanitize (lambda (value) (if (list-of-strings? (opensmtpd-table-values this-record)) (quote list-of-strings) (quote assoc-list)))))) #+END_SRC * I found this is the guix source code...I'm not sure what the (thunked) offers... #+BEGIN_SRC scheme ;; Configuration of an Xorg server. (define-record-type* <xorg-configuration> xorg-configuration make-xorg-configuration xorg-configuration? (modules xorg-configuration-modules ;list of packages (thunked) ; filter out modules not supported on current system (default (filter (lambda (p) (member (%current-system) (package-supported-systems p))) %default-xorg-modules))) (fonts xorg-configuration-fonts ;list of packges (default %default-xorg-fonts)) (drivers xorg-configuration-drivers ;list of strings (default '())) (resolutions xorg-configuration-resolutions ;list of tuples (default '())) (keyboard-layout xorg-configuration-keyboard-layout ;#f | <keyboard-layout> (default #f)) (extra-config xorg-configuration-extra-config ;list of strings (default '())) (server xorg-configuration-server ;package (default xorg-server)) (server-arguments xorg-configuration-server-arguments ;list of strings (default %default-xorg-server-arguments))) #+END_SRC