If clauses in the config

Hello,
I have declared an option to set if the device is being used at home, where I have my own DNS:

options.location.atHome = lib.mkOption {
    type = lib.types.bool;
    default = true;
  };

I would like to use it in this way:

if (options.location.atHome == false) then
  networking.nameservers = [ "9.9.9.9" "149.112.112.112" ];

however I get errors about Unexpected IF, and wrapping it within brackets also produces a syntax error.

Can anyone tell me how to do this sort of thing?

You can use lib.optionals from nixpkgs:

{ pkgs, lib, ... }:

{
  networking.nameservers = lib.optionals (options.location.atHome == false) [
    "9.9.9.9"
    "149.112.112.112"
  ];
}

lib.optionals p l evaluates to the empty list if predicate p is false, otherwise it evaluates to l.

1 Like

Okay.

Is there some way of grouping multiple attributes together, like

if atHome; then
  option1 = something1
  option2 = something2
else
  option3 = something3

Nix is an expression language, not a programming language. The end result of any piece of code must always be a value.

What’s the value of your expression when options.location.atHome = true?
It’s undefined? That’s not legal. Therefore, an if expression must always have an else branch.

The next part of your confusion is that the return value of your expression is an attrset binding which is not a valid data type in Nix. You can only do it the other way around; bind an attrset key to the value of an expression like this:

{
  foo = if cond then "bar" else "baz";
}

You could use lib.optionals as @jlode mentioned. It’s effectively the same as

networking.nameservers = if (options.location.atHome == false) then
  [ "9.9.9.9" "149.112.112.112" ]
else
  [ ];

For NixOS module purposes, there is the mkIf function which effectively “unsets” the value if the condition is false.

networking.nameservers = mkIf (options.location.atHome == false) [ "9.9.9.9" "149.112.112.112" ];

This would be considered cleaner since you’re explicitly saying that the value should only be touched if the condition is true; with optionals you’d explicitly be setting the value to [ ] eventhough you didn’t intend to set it at all.

Something like this is possible using mkMerge:

{
  config.foo = mkMerge [
    (mkIf atHome {
      option1 = something1;
      option2 = something2;
    })
    (mkIf !atHome {
      option3 = somethin3;
    })
  ];

Some people have written themselves a mkIfElse wrapper for this purpose. I proposed it should be added to Nixpkgs but I don’t think that has happened yet.

3 Likes

What is in your example works for “root” option foo.
Can I use it for multiple options, like:

    lib.mkIf atHome {
      ## 0. Disable IPv6
      boot.kernelParams = [ "ipv6.disable=1" ];
      networking.enableIPv6 = false;
      networking.dhcpcd.wait = "ipv4";
      ## 1. Make DHCPcd settings persistent 
      networking.dhcpcd.persistent = true;
    };

    lib.mkIf !atHome {
      networking.nameservers = [ "9.9.9.9" "149.112.112.112" ];
    };
{
  config = lib.mkMerge [
    (lib.mkIf atHome {
      ## 0. Disable IPv6
      boot.kernelParams = [ "ipv6.disable=1" ];
      networking.enableIPv6 = false;
      networking.dhcpcd.wait = "ipv4";
      ## 1. Make DHCPcd settings persistent 
      networking.dhcpcd.persistent = true;
    })

    (lib.mkIf !atHome {
      networking.nameservers = [ "9.9.9.9" "149.112.112.112" ];
    })
  };
}
1 Like

I have a primary config = {}.
When I ad a secondary one:

config = {
  networking.dhcpcd.persistent = true;
};

it works fine.

However if I use it with lib.mkMerge,

config = lib.mkMerge [
    { networking.dhcpcd.persistent = true; }
  ];

I get errors: error: attribute 'config' already defined at /etc/nixos/file.nix
I have read that there can be only one config, so is two working a side effect or something?

Where can I read up on the mkMerge?

I have also tried putting mkMerge on top – that worked.
Now i have

config = lib.mkMerge [
  {normal-config}
  (lib.mkIf !atHome {
      networking.nameservers = [ "9.9.9.9" "149.112.112.112" ];
    })
];

I get a syntax error unexpected '!', expecting ')'

For context, my option looks like this:

options = {
    atHome = lib.mkOption {
      type = lib.types.bool;
      default = true;
    };
  };

What is wrong?

The fine manual.

Yup, that’s the expected solution to the first half of this post.

In Nix, lib.mkIf !atHome is parsed as ‘expr operator expr’, and ! isn’t an infix binary operator. When using ! or prefix - at the beginning of an argument to a function, you need to wrap it in parentheses (much like Haskell, if you’re familiar): lib.mkIf (!atHome).

1 Like