I’m writing a module for my home-manager configuration to allow me to specify my Haskell toolchain in an idiomatic way. I wanted to write the config definition that corresponds to the options in a more modular way. I started out with this:
{
config = let cfg = config.programs.haskell;
in {
home.packages = optional cfg.cabal.enable cfg.cabal.package
++ optional cfg.ghc.enable (if cfg.ghc.package ? withPackages then
cfg.ghc.package.withPackages cfg.ghc.packages
else
cfg.ghc.package) ++ optional cfg.stack.enable cfg.stack.package;
warnings = if !cfg.ghc.package ? withPackages then [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''] else
[ ];
};
}
I looked on this module again when wanting to add HLS to it, but I felt it was too convoluted.
I wanted to separate the options using something like mkMerge
. I tried this:
{
config = let cfg = config.programs.haskell;
in mkMerge [
(mkIf cfg.ghc.enable (if cfg.ghc.package ? withPackages then {
config.home.packages =
[ (cfg.ghc.package.withPackages cfg.ghc.packages) ];
} else {
config.home.packages = [ cfg.ghc.package ];
warnings = [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''];
}))
(mkIf cfg.cabal.enable { config.home.packages = [ cfg.cabal.package ]; })
(mkIf cfg.stack.enable { config.home.packages = [ cfg.stack.package ]; })
(mkIf cfg.hls.enable { config.home.packages = [ cfg.hls.package ]; })
];
}
However, this throws due to infinite recursion. I then tried moving the binding site of config
further inwards:
{
config = mkMerge [
({ config, ... }:
mkIf config.programs.haskell.ghc.enable
(if config.programs.haskell.ghc.package ? withPackages then {
config.home.packages = [
(config.programs.haskell.ghc.package.withPackages
config.programs.haskell.packages)
];
} else {
config.home.packages = [ config.programs.haskell.ghc.package ];
warnings = [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''];
}))
({ config, ... }:
mkIf config.programs.haskell.cabal.enable {
config.home.packages = [ config.programs.haskell.cabal.package ];
})
({ config, ... }:
mkIf config.programs.haskell.stack.enable {
config.home.packages = [ config.programs.haskell.stack.package ];
})
({ config, ... }:
mkIf config.programs.haskell.hls.enable {
config.home.packages = [ config.programs.haskell.hls.package ];
})
];
}
But this errors out with a very cryptic message:
error: You're trying to declare a value of type `lambda'
rather than an attribute-set for the option
`'!
This usually happens if `' has option
definitions inside that are not matched. Please check how to properly define
this option by e.g. referring to `man 5 configuration.nix'!
At this point I realized that an issue was me declaring config
inside of config
. I then tried using imports to allow for warnings
and config
to be both defined by some of these modules.
{
imports = [
(mkMerge [
({ config, ... }:
mkIf config.programs.haskell.ghc.enable
(if config.programs.haskell.ghc.package ? withPackages then {
config.home.packages = [
(config.programs.haskell.ghc.package.withPackages
config.programs.haskell.packages)
];
} else {
config.home.packages = [ config.programs.haskell.ghc.package ];
warnings = [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''];
}))
({ config, ... }:
mkIf config.programs.haskell.cabal.enable {
config.home.packages = [ config.programs.haskell.cabal.package ];
})
({ config, ... }:
mkIf config.programs.haskell.stack.enable {
config.home.packages = [ config.programs.haskell.stack.package ];
})
({ config, ... }:
mkIf config.programs.haskell.hls.enable {
config.home.packages = [ config.programs.haskell.hls.package ];
})
])
];
}
This still didn’t work—the same error appeared. I tried omitting the mkMerge
:
config = [
({ config, ... }:
mkIf config.programs.haskell.ghc.enable
(if config.programs.haskell.ghc.package ? withPackages then {
config.home.packages = [
(config.programs.haskell.ghc.package.withPackages
config.programs.haskell.packages)
];
} else {
config.home.packages = [ config.programs.haskell.ghc.package ];
warnings = [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''];
}))
({ config, ... }:
mkIf config.programs.haskell.cabal.enable {
config.home.packages = [ config.programs.haskell.cabal.package ];
})
({ config, ... }:
mkIf config.programs.haskell.stack.enable {
config.home.packages = [ config.programs.haskell.stack.package ];
})
({ config, ... }:
mkIf config.programs.haskell.hls.enable {
config.home.packages = [ config.programs.haskell.hls.package ];
})
];
That gives the same error. So it’s probably because of the mkIf
s. I tried using optional
instead:
imports = optional config.programs.haskell.ghc.enable
(if config.programs.haskell.ghc.package ? withPackages then {
config.home.packages = [
(config.programs.haskell.ghc.package.withPackages
config.programs.haskell.packages)
];
} else {
config.home.packages = [ config.programs.haskell.ghc.package ];
warnings = [''
You have provided a package as programs.haskell.ghc.package that doesn't have the withPackages utility function.
This disables specifying packages via programs.haskell.ghc.packages.
''];
}) ++ optional config.programs.haskell.cabal.enable {
config.home.packages = [ config.programs.haskell.cabal.package ];
} ++ optional config.programs.haskell.stack.enable {
config.home.packages = [ config.programs.haskell.stack.package ];
} ++ optional config.programs.haskell.hls.enable {
config.home.packages = [ config.programs.haskell.hls.package ];
};
…but this gives infinite recursion again.
How can I achieve something like this without errors?
PS: I reposted this because I had posted it without a category. I hope this is better.