!wfudwzqQUiJYJnqfSY:nixos.org

NixOS Module System

209 Members
50 Servers

Load older messages


SenderMessageTime
29 May 2026
@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
@llakala:matrix.orgllakalaanyways, PR going up to your branch for nullOr18:51:52
@llakala:matrix.orgllakalahttps://github.com/MattSturgeon/nixpkgs/pull/1 up!19:04:49
@mattsturg:matrix.orgMatt Sturgeon

I also have an optimization that I'm not totally sure about. In the case that nulls.right is nonempty (meaning that some of the values are null), you do:

  valueMeta = optionalAttrs (nulls.wrong != [ ]) checkedAndMerged.valueMeta;

But if nulls.wrong != [], then we're going to get an error. [...]
So I think we can set valueMeta = { } here, and behavior will be unchanged.

I'm also not 100% on that. valueMeta should (typically) only be evaluated when introspecting options, or extending them in non-standard ways.

IIUC, it's supposed to work even if there are (unrelated) errors.

Originally, checkedAndMerged was checking & merging nulls.wrong, so nulls were already filtered out. Meaning it was possible for code that didn't care about our mixed-null-non-null assertion to still read what the elemType would've returned for valueMeta.

If that has a large performance cost, it may not be worth it; so you're optimisation may be acceptable. Ideally we'd get @hsjobeki or @roberth's input on idiomatic valueMeta in a partial/unrelated error scenario.

Looking at how it simplifies the overall diff, I personally think it's fine.

19:19:36
@mattsturg:matrix.orgMatt Sturgeon

lib.types: don't take loc parameter in checkDefsForError

I'd prefer we didn't increase the scope of my PR to include this. Do you mind moving that to a dedicated PR against nixpkgs?

19:19:38
@mattsturg:matrix.orgMatt SturgeonAre you happy for me to squash your changes into a "co-authored-by" commit, or would you prefer I merge your actual commits as-is? Typically we avoid having commits in a PR that fix earlier commits in the same PR, unless there's a specific story we want to tell in the git history.19:21:47

Show newer messages


Back to Room ListRoom Version: 10