!wfudwzqQUiJYJnqfSY:nixos.org

NixOS Module System

133 Members
27 Servers

You have reached the beginning of time (for this room).


SenderMessageTime
16 May 2025
@mattsturg:matrix.orgMatt Sturgeon

One way you could work around this is to add an internal final option within the submodule and then map to it in the apply function:

foo = mkOption {
  type = types.submodule (
    { config, options, ... }:
    {
      freeformType = types.attrsOf types.str;
      options.marker_a = mkOption {
        type = types.str;
      };
      options.marker_b = mkOption {
        type = types.str;
      };
      options.__result = mkOption {
        type = config._module.freeformType;
        internal = true;
      };

      config.__result =
        let
          cfg = builtins.removeAttrs config [
            "_module"
            "marker_a"
            "marker_b"
            "__result"
          ];
        in
        if options.marker_a.isDefined then
          cfg // { inherit (config) marker_a; } # TODO
        else if options.marker_b.isDefined then
          cfg // { inherit (config) marker_b; } # TODO
        else
          cfg # TODO: (empty? no marker?)
          ;
    }
  );

  apply = value: value.__result;
}
20:16:10
@ss:someonex.netSomeoneSerge (back on matrix) Yeah, thought about that. One'd need access to all defs at once... 20:18:26
@ss:someonex.netSomeoneSerge (back on matrix) H'm, do I get a merged value as an input if I addCheck (submodule ...) (def: def?marker_a)?
I just tried adding { foo = { }; } before and after { foo.marker_b = ...; } and it still seems to work
20:28:37
@h7x4:nani.wtfh7x4 I believe the attrs are merged before being typechecked. I suppose you could try adding a lib.trace inside the typecheck to verify? 20:32:31
@ss:someonex.netSomeoneSerge (back on matrix)
trace: { below = "_"; }                                                                                                                                                                                              
trace: { marker_b = "bar"; }                                                                                                                                                                                         
trace: { above = "_"; }                                                                                                                                                                                              
trace: { below = "_"; }                                                                                                                                                                                              
{ foo = { above = "_"; below = "_"; marker_b = "bar"; }; } 

For modules:

    { foo = { above = "_"; }; }
    {
      foo.marker_b = "bar";
    }
    { foo = { below = "_"; }; }

20:37:22
@ss:someonex.netSomeoneSerge (back on matrix) *
trace: { below = "_"; }                                                                                                                                                                                              
trace: { marker_b = "bar"; }                                                                                                                                                                                         
trace: { above = "_"; }                                                                                                                                                                                              
trace: { below = "_"; }                                                                                                                                                                                              
{ foo = { above = "_"; below = "_"; marker_b = "bar"; }; } 

For modules:

    { foo = { above = "_"; }; }
    {
      foo.marker_b = "bar";
    }
    { foo = { below = "_"; }; }

20:37:32
@mattsturg:matrix.orgMatt Sturgeon

This won't support foo = { config,...}: { marker_b = "bar"; }.

Type checking is done before merging, but after resolving mkIf (etc) wrappers.

Type checking is used to determine which type to use for merging.

20:39:18
@h7x4:nani.wtfh7x4Aha, that makes sense 👍️20:41:25
@ss:someonex.netSomeoneSerge (back on matrix) Sounds like __result is least wrong? 20:50:51
@ss:someonex.netSomeoneSerge (back on matrix) * Sounds like __result is the least wrong? 20:50:55
@ss:someonex.netSomeoneSerge (back on matrix) H'm what if freeformTypes are different? attrsOf a and attrsOf b 20:53:42
@mattsturg:matrix.orgMatt SturgeonI believe the freeform type can be conditional on a marker option being defined without infinite recursion.20:58:27
@mattsturg:matrix.orgMatt SturgeonSo long as the marker options are explicitly defined, they are resolved before freeform definitions 20:59:14
@ss:someonex.netSomeoneSerge (back on matrix)Not sure I should depend on this detail 😅20:59:19
@mattsturg:matrix.orgMatt Sturgeon* So long as the marker options are explicitly declared, they are resolved before freeform definitions 20:59:26
@mattsturg:matrix.orgMatt Sturgeon freeformType is itself an option (config._module.freeformType), so it's not really an implementation detail 21:00:17
@mattsturg:matrix.orgMatt Sturgeon
(
  { options, ... }:
  {
    freeformType = attrsOf (
      if options.marker_a.isDefined && options.marker_b.isDefined then
        throw "both `${options.marker_a}' and `${options.marker_b}' are defined"
      else if options.marker_a.isDefined then
        a
      else if options.marker_b.isDefined then
        b
      else
        throw "neither `${options.marker_a}' or `${options.marker_b}' are defined"
    );
  }
)
21:06:01
@mattsturg:matrix.orgMatt Sturgeon _Note: stringifying an option will use it's showOption opt.loc location, so you'll automatically get the full option prefix, e.g. "foo.marker_b" 21:07:27
@mattsturg:matrix.orgMatt Sturgeon * Note: stringifying an option will use it's showOption opt.loc location, so you'll automatically get the full option prefix, e.g. "foo.marker_b" 21:07:39
@ss:someonex.netSomeoneSerge (back on matrix)Awe and horror21:41:09
@ss:someonex.netSomeoneSerge (back on matrix) I suspect this works and then gets rejected by some kind of a final type check: error: The option marker_a' was accessed but has no value defined. Try setting the option` 22:04:36
@ss:someonex.netSomeoneSerge (back on matrix) * I suspect this works and then gets rejected by some kind of a final type check: error: The option `marker_a' was accessed but has no value defined. Try setting the option 22:04:51
@ss:someonex.netSomeoneSerge (back on matrix) *

I suspect this works and then gets rejected by some kind of a final type check: error: The option `marker_a' was accessed but has no value defined. Try setting the option

(my attempt: https://gist.github.com/SomeoneSerge/29a9e6e10cdcd3596f865c41c7042610)

22:05:59
@mattsturg:matrix.orgMatt Sturgeon

That's not a type check, that's an "empty value" stub. When an option has no definitions, its value is set to a stub that throws that error.

You're probably reading the value somewhere, e.g. using config.marker_a instead of options.marker_a, or not using removeAttrs when creating __result?

22:06:40
@mattsturg:matrix.orgMatt Sturgeon On line 11 you assign freeformType to a submodule, but freeformType must be an attrsOf type. 22:12:06
@mattsturg:matrix.orgMatt Sturgeon

On line 45 you're defining apply which is meaningless in a module, it's a feature of options.

If you're evaluating these directly with lib.evalModules instead of using a submodule option, you can do the apply manually:

(lib.evalModules { /* ... */ }).config.__result
22:14:08
@ss:someonex.netSomeoneSerge (back on matrix)

On line 45 you're defining apply which is meaningless in a module, it's a feature of options.

Yeah I was wondering if I misunderstood the "freeformType is an option"

22:15:13
@ss:someonex.netSomeoneSerge (back on matrix)Whoops, the snippet is screwed then I guess22:15:50

Show newer messages


Back to Room ListRoom Version: 10