mkIf vs if / then

Hi folks,

It’s obvious to me that mkIf makes more sense when I don’t need else:

config = {
  environment.systemPackages =
    mkIf config.my.custom.opt.installvim
    [ pkgs.vim ];
}

and if / then makes more sense when I do need else:

config = {
  services.resolved.dnssec =
    if config.my.custom.opt.dnssecforced
    then "true"
    else "allow-downgrade";
}

or

config = {
  services.resolved =
    if config.my.custom.opt.resolved
    then {
      enable = true;
      llmnr = "false";
      dnssec = "allow-downgrade";
    }
    else {
      enable = false;
    }
}

But are there any other considerations? Reading between lines in other posts, it seems that mkIf is prefered when it comes to NixOS configuration. But I’m not sure why…

Thank you.

Michael

The implicit else branch of lib.mkIf is the least important difference between that and if-then-else construct. You could achieve the same with lib.optionalAttrs, which has the same interface:

Rather, the main difference is that mkIf produces an attribute set that is handled specially by the NixOS module system and keeps the contents available:

This will allow the module system to remain aware of the contents even when the condition is false and it can validate them.

That is also one of the reasons mkIf is preferred in NixOS modules. The other is that evaluation of the condition can be more lazy.

Just note that mkIf produces a special attribute set so the result will need to be merged with lib.mkMerge – you can no longer just use standard Nix operators:

config = {
  environment.systemPackages = [
    pkgs.ripgrep
  # ⬐ THIS IS WRONG!
  ] ++ mkIf config.my.custom.opt.installvim [
    pkgs.vim
  ];
}
5 Likes

https://nixos.org/manual/nixos/stable/#sec-option-definitions-delaying-conditionals

The issue described in this section can come up in surprising ways sometimes, so it’s best to default to mkIf whenever possible (though it’d be cool if we had a mkIfElse function that took a second argument for the else case)

1 Like
mkIfElse = p: yes: no: mkMerge [
  (mkIf p yes)
  (mkIf (!p) no)
];

I usually just use this construct inline, rather than calling a function for it.

3 Likes

I’m sure almost everyone has their own mkIfElse and mkUnless :slight_smile:

1 Like

Time to add them to lib?