!wfudwzqQUiJYJnqfSY:nixos.org

NixOS Module System

204 Members
47 Servers

Load older messages


SenderMessageTime
17 Apr 2026
@c4lliope:matrix.orgc4lliope changed their profile picture.08:41:53
@robin:robin.townRobin joined the room.18:57:51
@robin:robin.townRobin left the room.19:04:02
18 Apr 2026
@lukas:landgraf.moeLukas joined the room.01:17:12
@rasmata:matrix.orgSienou Rasmata joined the room.15:53:09
@rasmata:matrix.orgSienou Rasmata left the room.15:53:12
23 Apr 2026
@maxisthespy:matrix.orgMaxisthespy joined the room.21:25:27
27 Apr 2026
@ninja:worldethicaldataforum.orgNinja joined the room.14:34:01
30 Apr 2026
@isabel:isabelroses.comisabel changed their profile picture.18:47:35
1 May 2026
@llakala:matrix.orgllakala set a profile picture.17:12:42
3 May 2026
@isabel:isabelroses.comisabel changed their profile picture.10:39:10
8 May 2026
@jopejoe1:matrix.orgjopejoe1 changed their display name from jopejoe1 (4094@epvpn) to jopejoe1.08:46:19
9 May 2026
@me:linj.techlinj joined the room.05:02:21
11 May 2026
@maelys_pan:matrix.orgMaëlys joined the room.20:40:21
12 May 2026
@artify:artify.zoneRichard Tichý joined the room.11:19:41
@koutensky:matrix.nesad.fit.vutbr.czMichal Koutenský joined the room.18:16:59
14 May 2026
@cwtuh:matrix.orgtuhana joined the room.15:12:49
19 May 2026
@snow101:matrix.organurag left the room.08:37:05
@amadaluzia:4d2.orgamadaluzia changed their profile picture.20:58:55
22 May 2026
@polykernel:kde.orgpolykernel joined the room.16:40:58
@polykernel:kde.orgpolykernel What is the best way to propagate checks added by addCheck in the type merge process? I tried overriding functor in the extended type but the functor attached to the type after merging options declarations is not the one I overwrote. 17:00:24
@polykernel:kde.orgpolykernel

Here is a MWE:

{
  pkgs ? import <nixpkgs> { },
  lib ? pkgs.lib,
}:

let
  testSubmodule =
    { config, ... }:
    {
      freeformType = lib.types.lazyAttrsOf lib.types.raw;

      options.a = lib.mkOption {
        type = lib.types.int;
        default = 0;
      };
    };

  isSubmoduleFn =
    m:
    let
      args = builtins.functionArgs m;
    in
    args ? lib || args ? config || args ? options;

  staticInnerType =
    let
      baseType = lib.types.addCheck (lib.types.submodule testSubmodule) (
        v: builtins.isAttrs v || isSubmoduleFn v
      );
    in
    baseType
    // rec {
      functor = baseType.functor // {
        type =
          attrs:
          let
            mergedType = lib.types.addCheck (lib.types.submoduleWith attrs) (
              v: builtins.isAttrs v || isSubmoduleFn v
            );
          in
          mergedType
          // {
            inherit substSubModules;
            functor = mergedType.functor // {
              inherit (functor) type;
            };
          };
      };

      substSubModules =
        m:
        lib.types.addCheck (lib.types.submodule m) (v: builtins.isAttrs v || isSubmoduleFn v)
        // {
          inherit substSubModules;
        };
    };

  dynamicInnerType = lib.types.functionTo staticInnerType;

  innerType = lib.types.either staticInnerType dynamicInnerType // rec {
    substSubModules =
      m:
      (lib.types.either (staticInnerType.substSubModules m) (dynamicInnerType.substSubModules m))
      // {
        inherit substSubModules;
      };
  };

  testType = lib.types.lazyAttrsOf innerType;

  optionModule1 = {
    options.test = lib.mkOption {
      type = testType;
    };
  };

  optionModule2 = {
    options.test = lib.mkOption {
      type = testType.substSubModules [
        {
          options.b = lib.mkOption {
            type = lib.types.bool;
          };
        }
      ];
    };
  };

  staticConfig = {
    config.test.random2 = {
      b = false;
    };
  };

  dynamicConfig = {
    config.test.random1 =
      { arg1 }:
      {
        a = 1;
      };
  };

  result = lib.evalModules {
    modules = [
      optionModule1
      optionModule2

      staticConfig
      dynamicConfig
    ];
  };
in
result
17:01:39
@polykernel:kde.orgpolykernel The check on staticInnerType gets dropped after the type merge. 17:02:19
@polykernel:kde.orgpolykernel * What is the best way to propagate checks added by addCheck in the type merge process? I tried overriding functor in the extended type but the functor attached to the type after merging options declarations is not one supplied in my override (I am not sure why this is the case?). 17:06:14
@jonhermansen:matrix.orgJon Hermansen changed their display name from jonhermansen to Jon Hermansen.19:18:49
@polykernel:kde.orgpolykernel

After some reading, I realized I needed to override typeMerge as overriding functor after mkOptionType has materialized does not change the typeMerge field. I ended up making a function that builds a custom submodule type instead of shallowing attributes. Here is the updated example that works in case it helps anyone in the future:

{
  pkgs ? import /home/sheaf/projects/github/nixpkgs { },
  lib ? pkgs.lib,
}:

let
  testSubmodule =
    { config, ... }:
    {
      freeformType = lib.types.lazyAttrsOf lib.types.raw;

      options.a = lib.mkOption {
        type = lib.types.int;
        default = 0;
      };
    };

  isSubmoduleFn =
    m:
    let
      args = builtins.functionArgs m;
    in
    args ? lib || args ? config || args ? options;

  customSubmoduleWith =
    attrs:
    let
      t = lib.types.addCheck (lib.types.submoduleWith attrs) (v: builtins.isAttrs v || isSubmoduleFn v);
    in
    t
    // rec {
      substSubmodules = m: customSubmoduleWith (attrs // { modules = m; });
      functor = t.functor // {
        type = customSubmoduleWith;
      };
      typeMerge = lib.types.defaultTypeMerge functor;
    };

  customSubmodule =
    module:
    customSubmoduleWith {
      shorthandOnlyDefinesConfig = true;
      modules = [ module ];
    };

  staticInnerType = customSubmodule testSubmodule;

  dynamicInnerType = lib.types.functionTo staticInnerType;

  innerType = lib.types.either staticInnerType dynamicInnerType;

  testType = lib.types.lazyAttrsOf innerType;

  optionModule1 = {
    options.test = lib.mkOption {
      type = testType;
    };
  };

  optionModule2 = {
    options.test = lib.mkOption {
      type =
        let
          t = customSubmodule {
            options.b = lib.mkOption {
              type = lib.types.bool;
            };
          };
        in
        lib.types.lazyAttrsOf (lib.types.either t (lib.types.functionTo t));
    };
  };

  staticConfig = {
    config.test.random2 = {
      b = false;
    };
  };

  dynamicConfig = {
    config.test.random1 =
      { arg1 }:
      {
        a = 1;
      };
  };

  result = lib.evalModules {
    modules = [
      optionModule1
      optionModule2

      staticConfig
      dynamicConfig
    ];
  };
in
result
21:50:44
@polykernel:kde.orgpolykernelRedacted or Malformed Event21:50:54
@polykernel:kde.orgpolykernel I found where my error is. I need to override typeMerge as well since shallowing functor after the mkOptionType call won't affect the typeMerge field. 21:53:07
@polykernel:kde.orgpolykernel

Here is the updated working example in case it helps anyone in the future:

{
  pkgs ? import <nixpkgs> { },
  lib ? pkgs.lib,
}:

let
  testSubmodule =
    { config, ... }:
    {
      freeformType = lib.types.lazyAttrsOf lib.types.raw;

      options.a = lib.mkOption {
        type = lib.types.int;
        default = 0;
      };
    };

  isSubmoduleFn =
    m:
    let
      args = builtins.functionArgs m;
    in
    args ? lib || args ? config || args ? options;

  customSubmoduleWith =
    attrs:
    let
      t = lib.types.addCheck (lib.types.submoduleWith attrs) (v: builtins.isAttrs v || isSubmoduleFn v);
    in
    t
    // rec {
      substSubmodules = m: customSubmoduleWith (attrs // { modules = m; });
      functor = t.functor // {
        type = customSubmoduleWith;
      };
      typeMerge = lib.types.defaultTypeMerge functor;
    };

  customSubmodule =
    module:
    customSubmoduleWith {
      shorthandOnlyDefinesConfig = true;
      modules = [ module ];
    };

  staticInnerType = customSubmodule testSubmodule;

  dynamicInnerType = lib.types.functionTo staticInnerType;

  innerType = lib.types.either staticInnerType dynamicInnerType;

  testType = lib.types.lazyAttrsOf innerType;

  optionModule1 = {
    options.test = lib.mkOption {
      type = testType;
    };
  };

  optionModule2 = {
    options.test = lib.mkOption {
      type =
        let
          t = customSubmodule {
            options.b = lib.mkOption {
              type = lib.types.bool;
            };
          };
        in
        lib.types.lazyAttrsOf (lib.types.either t (lib.types.functionTo t));
    };
  };

  staticConfig = {
    config.test.random2 = {
      b = false;
    };
  };

  dynamicConfig = {
    config.test.random1 =
      { arg1 }:
      {
        a = 1;
      };
  };

  result = lib.evalModules {
    modules = [
      optionModule1
      optionModule2

      staticConfig
      dynamicConfig
    ];
  };
in
result
21:54:16
@hsjobeki:matrix.orghsjobekihttps://github.com/NixOS/nixpkgs/issues/396021 This limitation of addCheck is known. Fyi the more "idiomatic" way is to use mkOptionType to create a custom type such as it is done in lib/types.nix itself. 22:06:04

Show newer messages


Back to Room ListRoom Version: 10