lib: introduce forEach
= flip map (#64723)
* lib: introduce `foreach` = flip map
The main purpose is to bring attention to `flip map`, which improves
code readablity. It is useful when ad-hoc anonymous function
grows two or more lines in `map` application:
```
map (lcfg:
let port = lcfg.port;
portStr = if port != defaultPort then ":${toString port}" else "";
scheme = if cfg.enableSSL then "https" else "http";
in "${scheme}://cfg.hostName${portStr}"
) (getListen cfg);
```
Compare this to `foreach`-style:
```
foreach (getListen cfg) (lcfg:
let port = lcfg.port;
portStr = if port != defaultPort then ":${toString port}" else "";
scheme = if cfg.enableSSL then "https" else "http";
in "${scheme}://cfg.hostName${portStr}"
);
```
This is similar to Haskell's `for` (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html#v:for)
* mass replace "flip map -> foreach"
See `foreach`-introduction commit.
```
rg 'flip map ' --files-with-matches | xargs sed -i 's/flip map /foreach /g'
```
* Revert "mass replace "flip map -> foreach""
This reverts commit 3b0534310c
.
* mass replace "flip map -> forEach"
See `forEach`-introduction commit.
```
rg 'flip map ' --files-with-matches | xargs sed -i 's/flip map /forEach /g'
```
* rename foreach -> forEach
* and one more place
* add release notes
This commit is contained in:
commit
d09b4e3c87
@ -71,7 +71,7 @@ let
|
||||
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
||||
recursiveUpdate matchAttrs overrideExisting getOutput getBin
|
||||
getLib getDev chooseDevOutputs zipWithNames zip;
|
||||
inherit (lists) singleton foldr fold foldl foldl' imap0 imap1
|
||||
inherit (lists) singleton forEach foldr fold foldl foldl' imap0 imap1
|
||||
concatMap flatten remove findSingle findFirst any all count
|
||||
optional optionals toList range partition zipListsWith zipLists
|
||||
reverseList listDfs toposort sort naturalSort compareLists take
|
||||
|
@ -21,6 +21,19 @@ rec {
|
||||
*/
|
||||
singleton = x: [x];
|
||||
|
||||
/* Apply the function to each element in the list. Same as `map`, but arguments
|
||||
flipped.
|
||||
|
||||
Type: forEach :: [a] -> (a -> b) -> [b]
|
||||
|
||||
Example:
|
||||
forEach [ 1 2 ] (x:
|
||||
toString x
|
||||
)
|
||||
=> [ "1" "2" ]
|
||||
*/
|
||||
forEach = xs: f: map f xs;
|
||||
|
||||
/* “right fold” a binary function `op` between successive elements of
|
||||
`list` with `nul' as the starting value, i.e.,
|
||||
`foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
|
||||
|
@ -424,6 +424,18 @@
|
||||
installer after creating <literal>/var/lib/nextcloud</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
There exists now <literal>lib.forEach</literal>, which is like <literal>map</literal>, but with
|
||||
arguments flipped. When mapping function body spans many lines (or has nested
|
||||
<literal>map</literal>s), it is often hard to follow which list is modified.
|
||||
</para>
|
||||
<para>
|
||||
Previous solution to this problem was either to use <literal>lib.flip map</literal>
|
||||
idiom or extract that anonymous mapping function to a named one. Both can still be used
|
||||
but <literal>lib.forEach</literal> is preferred over <literal>lib.flip map</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -54,11 +54,11 @@ rec {
|
||||
|
||||
machinesNumbered = zipLists machines (range 1 254);
|
||||
|
||||
nodes_ = flip map machinesNumbered (m: nameValuePair m.fst
|
||||
nodes_ = forEach machinesNumbered (m: nameValuePair m.fst
|
||||
[ ( { config, nodes, ... }:
|
||||
let
|
||||
interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
|
||||
interfaces = flip map interfacesNumbered ({ fst, snd }:
|
||||
interfaces = forEach interfacesNumbered ({ fst, snd }:
|
||||
nameValuePair "eth${toString snd}" { ipv4.addresses =
|
||||
[ { address = "192.168.${toString fst}.${toString m.snd}";
|
||||
prefixLength = 24;
|
||||
@ -88,7 +88,7 @@ rec {
|
||||
"${config.networking.hostName}\n"));
|
||||
|
||||
virtualisation.qemu.options =
|
||||
flip map interfacesNumbered
|
||||
forEach interfacesNumbered
|
||||
({ fst, snd }: qemuNICFlags snd fst m.snd);
|
||||
};
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ let
|
||||
# builtins multiply by 4 the memory usage and the time used to compute
|
||||
# each options.
|
||||
tryCollectOptions = moduleResult:
|
||||
flip map (excludeOptions (collect isOption moduleResult)) (opt:
|
||||
forEach (excludeOptions (collect isOption moduleResult)) (opt:
|
||||
{ name = showOption opt.loc; } // builtins.tryEval (strict opt.value));
|
||||
in
|
||||
keepNames (
|
||||
|
@ -276,7 +276,7 @@ with lib;
|
||||
throw "services.redshift.longitude is set to null, you can remove this"
|
||||
else builtins.fromJSON value))
|
||||
|
||||
] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
|
||||
] ++ (forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
|
||||
"jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
|
||||
"snmpExporter" "unifiExporter" "varnishExporter" ]
|
||||
(opt: mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
|
||||
|
@ -225,7 +225,7 @@ in
|
||||
''
|
||||
maxstartdelay = ${toString cfg.maxStartDelay}
|
||||
|
||||
${flip concatStringsSep (flip map (attrValues cfg.ups) (ups: ups.summary)) "
|
||||
${flip concatStringsSep (forEach (attrValues cfg.ups) (ups: ups.summary)) "
|
||||
|
||||
"}
|
||||
'';
|
||||
|
@ -15,7 +15,7 @@ let
|
||||
++ cfg.extraConfigFiles;
|
||||
|
||||
devices = attrValues (filterAttrs (_: i: i != null) cfg.interface);
|
||||
systemdDevices = flip map devices
|
||||
systemdDevices = forEach devices
|
||||
(i: "sys-subsystem-net-devices-${utils.escapeSystemdPath i}.device");
|
||||
in
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ let
|
||||
inherit (lib) concatStringsSep optionalString;
|
||||
|
||||
cfg = config.services.hylafax;
|
||||
mapModems = lib.flip map (lib.attrValues cfg.modems);
|
||||
mapModems = lib.forEach (lib.attrValues cfg.modems);
|
||||
|
||||
mkConfigFile = name: conf:
|
||||
# creates hylafax config file,
|
||||
|
@ -502,7 +502,7 @@ in
|
||||
|
||||
assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
|
||||
message = "cannot enable X11 forwarding without setting xauth location";}]
|
||||
++ flip map cfg.listenAddresses ({ addr, ... }: {
|
||||
++ forEach cfg.listenAddresses ({ addr, ... }: {
|
||||
assertion = addr != null;
|
||||
message = "addr must be specified in each listenAddresses entry";
|
||||
});
|
||||
|
@ -129,7 +129,7 @@ in
|
||||
assertion = cfg.killer != null -> cfg.killtime >= 10;
|
||||
message = "killtime has to be at least 10 minutes according to `man xautolock`";
|
||||
}
|
||||
] ++ (lib.flip map [ "locker" "notifier" "nowlocker" "killer" ]
|
||||
] ++ (lib.forEach [ "locker" "notifier" "nowlocker" "killer" ]
|
||||
(option:
|
||||
{
|
||||
assertion = cfg."${option}" != null -> builtins.substring 0 1 cfg."${option}" == "/";
|
||||
|
@ -78,7 +78,7 @@ let
|
||||
in imap1 mkHead cfg.xrandrHeads;
|
||||
|
||||
xrandrDeviceSection = let
|
||||
monitors = flip map xrandrHeads (h: ''
|
||||
monitors = forEach xrandrHeads (h: ''
|
||||
Option "monitor-${h.config.output}" "${h.name}"
|
||||
'');
|
||||
# First option is indented through the space in the config but any
|
||||
|
@ -684,7 +684,7 @@ in
|
||||
assertion = if args.efiSysMountPoint == null then true else hasPrefix "/" args.efiSysMountPoint;
|
||||
message = "EFI paths must be absolute, not ${args.efiSysMountPoint}";
|
||||
}
|
||||
] ++ flip map args.devices (device: {
|
||||
] ++ forEach args.devices (device: {
|
||||
assertion = device == "nodev" || hasPrefix "/" device;
|
||||
message = "GRUB devices must be absolute paths, not ${device} in ${args.path}";
|
||||
}));
|
||||
|
@ -74,7 +74,7 @@ in
|
||||
enable = true;
|
||||
networks."99-main" = genericNetwork mkDefault;
|
||||
}
|
||||
(mkMerge (flip map interfaces (i: {
|
||||
(mkMerge (forEach interfaces (i: {
|
||||
netdevs = mkIf i.virtual ({
|
||||
"40-${i.name}" = {
|
||||
netdevConfig = {
|
||||
@ -90,7 +90,7 @@ in
|
||||
name = mkDefault i.name;
|
||||
DHCP = mkForce (dhcpStr
|
||||
(if i.useDHCP != null then i.useDHCP else cfg.useDHCP && interfaceIps i == [ ]));
|
||||
address = flip map (interfaceIps i)
|
||||
address = forEach (interfaceIps i)
|
||||
(ip: "${ip.address}/${toString ip.prefixLength}");
|
||||
networkConfig.IPv6PrivacyExtensions = "kernel";
|
||||
} ];
|
||||
@ -102,7 +102,7 @@ in
|
||||
Kind = "bridge";
|
||||
};
|
||||
};
|
||||
networks = listToAttrs (flip map bridge.interfaces (bi:
|
||||
networks = listToAttrs (forEach bridge.interfaces (bi:
|
||||
nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
|
||||
DHCP = mkOverride 0 (dhcpStr false);
|
||||
networkConfig.Bridge = name;
|
||||
@ -173,7 +173,7 @@ in
|
||||
|
||||
};
|
||||
|
||||
networks = listToAttrs (flip map bond.interfaces (bi:
|
||||
networks = listToAttrs (forEach bond.interfaces (bi:
|
||||
nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
|
||||
DHCP = mkOverride 0 (dhcpStr false);
|
||||
networkConfig.Bond = name;
|
||||
|
@ -926,7 +926,7 @@ in
|
||||
warnings = concatMap (i: i.warnings) interfaces;
|
||||
|
||||
assertions =
|
||||
(flip map interfaces (i: {
|
||||
(forEach interfaces (i: {
|
||||
# With the linux kernel, interface name length is limited by IFNAMSIZ
|
||||
# to 16 bytes, including the trailing null byte.
|
||||
# See include/linux/if.h in the kernel sources
|
||||
@ -934,12 +934,12 @@ in
|
||||
message = ''
|
||||
The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters.
|
||||
'';
|
||||
})) ++ (flip map slaveIfs (i: {
|
||||
})) ++ (forEach slaveIfs (i: {
|
||||
assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ];
|
||||
message = ''
|
||||
The networking.interfaces."${i.name}" must not have any defined ips when it is a slave.
|
||||
'';
|
||||
})) ++ (flip map interfaces (i: {
|
||||
})) ++ (forEach interfaces (i: {
|
||||
assertion = i.preferTempAddress -> cfg.enableIPv6;
|
||||
message = ''
|
||||
Temporary addresses are only needed when IPv6 is enabled.
|
||||
@ -967,8 +967,8 @@ in
|
||||
"net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
|
||||
"net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
|
||||
} // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
|
||||
(i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)))
|
||||
// listToAttrs (flip map (filter (i: i.preferTempAddress) interfaces)
|
||||
(i: forEach [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)))
|
||||
// listToAttrs (forEach (filter (i: i.preferTempAddress) interfaces)
|
||||
(i: nameValuePair "net.ipv6.conf.${i.name}.use_tempaddr" 2));
|
||||
|
||||
# Capabilities won't work unless we have at-least a 4.3 Linux
|
||||
@ -1050,7 +1050,7 @@ in
|
||||
${cfg.localCommands}
|
||||
'';
|
||||
};
|
||||
} // (listToAttrs (flip map interfaces (i:
|
||||
} // (listToAttrs (forEach interfaces (i:
|
||||
let
|
||||
deviceDependency = if (config.boot.isContainer || i.name == "lo")
|
||||
then []
|
||||
|
@ -21,7 +21,7 @@ let
|
||||
useNetworkd = networkd;
|
||||
firewall.checkReversePath = true;
|
||||
firewall.allowedUDPPorts = [ 547 ];
|
||||
interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n:
|
||||
interfaces = mkOverride 0 (listToAttrs (forEach vlanIfs (n:
|
||||
nameValuePair "eth${toString n}" {
|
||||
ipv4.addresses = [ { address = "192.168.${toString n}.1"; prefixLength = 24; } ];
|
||||
ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
|
||||
|
@ -56,7 +56,7 @@ let
|
||||
init = let
|
||||
init = builtins.replaceStrings [ "\n" ] [ ";" ] (config.init or "");
|
||||
|
||||
mkScript = drv: lib.flip map drv.scripts (script: "/script load ${drv}/share/${script}");
|
||||
mkScript = drv: lib.forEach drv.scripts (script: "/script load ${drv}/share/${script}");
|
||||
|
||||
scripts = builtins.concatStringsSep ";" (lib.foldl (scripts: drv: scripts ++ mkScript drv)
|
||||
[ ] (config.scripts or []));
|
||||
|
@ -58,7 +58,7 @@ let
|
||||
versions = [ "13.8.0" "13.9.0" "13.9.1" ];
|
||||
in
|
||||
lib.listToAttrs
|
||||
(lib.flip map versions
|
||||
(lib.forEach versions
|
||||
(v: lib.nameValuePair v (throw "Unsupported citrix_receiver version: ${v}")));
|
||||
in
|
||||
deprecatedVersions // supportedVersions;
|
||||
|
@ -62,7 +62,7 @@ let
|
||||
versions = [ ];
|
||||
in
|
||||
lib.listToAttrs
|
||||
(lib.flip map versions
|
||||
(lib.forEach versions
|
||||
(v: lib.nameValuePair v (throw "Unsupported citrix_workspace version: ${v}")));
|
||||
in
|
||||
deprecatedVersions // supportedVersions;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
let
|
||||
|
||||
hunspellDirs = with lib; makeSearchPath ":" (flatten (flip map langs (lang: [
|
||||
hunspellDirs = with lib; makeSearchPath ":" (flatten (forEach langs (lang: [
|
||||
"${hunspellDicts.${lang}}/share/hunspell"
|
||||
"${hunspellDicts.${lang}}/share/myspell"
|
||||
"${hunspellDicts.${lang}}/share/myspell/dicts"
|
||||
|
Loading…
Reference in New Issue
Block a user