!wfudwzqQUiJYJnqfSY:nixos.org

NixOS Module System

208 Members
49 Servers

Load older messages


SenderMessageTime
29 May 2026
@toonn:matrix.orgtoonn That's null because it's a nullOr submodule, no? 12:38:20
@mattsturg:matrix.orgMatt Sturgeon it's a nullOr deferredType. In this case I assume it's null because the services.pixelfed.nginx's submodule-type isn't freeform. 12:39:24
@toonn:matrix.orgtoonn You're right, that one's not freeform. 12:40:24
@mattsturg:matrix.orgMatt Sturgeon * it's a nullOr optionType. In this case I assume it's null because the services.pixelfed.nginx's submodule-type isn't freeform. 12:40:29
@toonn:matrix.orgtoonn OK, so if I want to treat things uniformly getSubOptions might be easier than figuring out where in the functor payloads the information can be found. I'll be trying that and probably run into more questions : ) Thank you! 12:42:43
@mattsturg:matrix.orgMatt Sturgeon

where in the functor payloads the information can be found

the final merged configuration isn't visible to the functor. Because it only exists after merging option definitions. Option definitions can only exist once a type is used with an option.

12:43:46
@mattsturg:matrix.orgMatt Sturgeon *

where in the functor payloads the information can be found

the final merged configuration isn't visible to the functor. Because it only exists after merging option definitions. Option definitions can only exist once a type is used with an option.
So if you must do this from the type's perspective, the base-configuration (pre option-merge) is all you have access to.

12:44:14
@toonn:matrix.orgtoonn I'm starting from (nixos {}).options.services.<module>, IIUC those are merged option definitions, just may not be complete yet, missing some required definitions? 12:46:26
@mattsturg:matrix.orgMatt Sturgeon

The functor you referred to is options.services.<module>.type.functor, right? Note that you've stepped from the option (options.services.<module>) into its type .type. The type's functor is defined when the type is created, before any options are in the picture.

The option itself has access to it's final merged value (options.services.<module>.value) and if its type supports v2 check+merge, you may also have merged metadata (options.services.<module>.valueMeta). Note how none of that is under .type.

Once you return options.services.<module>.type.getSubOptions options.services.<module>.loc you have a new set of options that came from the type's base configuration; you've lost all knowledge of the owner option, its definitions, merged value, or merged metadata.

12:50:48
@toonn:matrix.orgtoonn Except for the `loc` no? 12:52:22
@mattsturg:matrix.orgMatt SturgeonSure, but that's just a list of strings, not an option.12:52:41
@toonn:matrix.orgtoonn I don't think I have a need for that information once I recurse, since what I return will be under the <module> or whatever nested option. 12:53:17
@mattsturg:matrix.orgMatt Sturgeon * Sure, but that's just a list of strings, not an option. It's useful only for properly prefixing the sub-options' own locs, for user-facing clarity. 12:53:20
@toonn:matrix.orgtoonn getSubOptions doesn't return a flat structure of all nested options, right? So I don't need to sort them by loc to regain the structure? 12:55:03
@mattsturg:matrix.orgMatt Sturgeon

I always recommend trying to keep the prefix accurate, so that once you have your final list of options you can do "${opt}" or showOption opt.loc and get the correct option attrpath. But you're right, functionally it makes no difference.
Having correct locs can be useful in attrsOf submodule scenarios, too, where we typically add "<name>" to the prefix. Or listOf submodule where we add "*" to the prefix.

In my interpretation of getSubOptions, it makes no guarantee of its structure. Imagine a type like either or attrTag, where different submodules could in theory be supported. It may make sense for such a type's getSubOptions to return an attrset of wrapped-type-name -> sub-options to avoid naming conflicts.

I'm not aware of any types that do that currently, but it's something to be aware of.

12:59:57
@mattsturg:matrix.orgMatt Sturgeon *

I always recommend trying to keep the prefix accurate, so that once you have your final list of options you can do "${opt}" or showOption opt.loc and get the correct option attrpath. But you're right, functionally it makes no difference.
Having correct locs can be useful in attrsOf submodule scenarios, too, where we typically add "<name>" to the prefix. Or listOf submodule where we add "*" to the prefix.

This may be controversial, but in my interpretation of getSubOptions, it makes no guarantee of its structure. Imagine a type like either or attrTag, where different submodules could in theory be supported. It may make sense for such a type's getSubOptions to return an attrset of wrapped-type-name -> sub-options to avoid naming conflicts.

I'm not aware of any types that do that currently, but it's something to be aware of.

13:00:23
@toonn:matrix.orgtoonn You mean something like attrsOf (either submodule submodule)? 13:24:25
@mattsturg:matrix.orgMatt Sturgeon

Yeah. Although currently that's not very common because it's not easy to discriminate between submodule-specific module definitions. A more practical example may be

either (attrsOf submoduleA) (listOf submoduleB)
13:34:48
@llakala:matrix.orgllakalais it an axiom that merging a single definition just results in the first definition's value?18:35:04
@mattsturg:matrix.orgMatt Sturgeon merge functions can also transform definitions, but I'd say the value should always be derived from that definition's value. Unless the merge function throws or something. 18:36:24
@mattsturg:matrix.orgMatt Sturgeon * merge functions can also transform definitions,* but I'd say the value should always be derived from that definition's value. Unless the merge function throws or something.
* e.g. types.coercedTo
18:36:49
@llakala:matrix.orgllakala

gotcha, i was foolishly hoping for a

                value = if length defs == 1 then (head defs).value else elemType.merge loc defs;
18:42:10
@llakala:matrix.orgllakalawe could do this with a special type attribute (unchangedOnSingleton) but it's certainly hacky18:43:13
@mattsturg:matrix.orgMatt Sturgeon * merge functions can also transform definitions,* but I'd say the value should always be derived from that definition's value. Unless the merge function throws or something.
* e.g. types.coercedTo, types.submodule, etc
18:43:49
@llakala:matrix.orgllakala like a lot of the basic types are defined with mergeEqualOption, which could employ this kind of short circuiting 18:45:44
@llakala:matrix.orgllakalabut i'm really just trying to win back performance on a PR that can't18:46:14
@llakala:matrix.orgllakalait does that anyways, just after another function call18:46:30
@mattsturg:matrix.orgMatt Sturgeon

I was gonna say something similar:

Wouldn't that only buy you ~1 lambda invocation per option (the elemType.merge call)? The elemType's merge function itself should already be doing something trivial in trivial cases

18:47:51
@mattsturg:matrix.orgMatt Sturgeon IIRC, there were already some performance vs convenience conversations in the original v2 check & merge PRs. With more types implementing v2, we need to call actual merge functions in order to check for errors anyway; so it's going to be hard to have zero performance hit. 18:49:50
@llakala:matrix.orgllakala yeah there's lots of small things - every merge seems to cost another function call with v2, because from my testing, functors always get called with self, not just once, and isV2MergeCoherent = true; gets set for v2 merges 18:51:47

Show newer messages


Back to Room ListRoom Version: 10