Skip to content

Commit 20f9370

Browse files
authored
lib.fixedPoints.toExtension: init (#336414)
2 parents 9a5abff + e31ace5 commit 20f9370

5 files changed

Lines changed: 101 additions & 41 deletions

File tree

lib/default.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ let
7979
fromHexString toHexString toBaseDigits inPureEvalMode isBool isInt pathExists
8080
genericClosure readFile;
8181
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
82-
composeManyExtensions makeExtensible makeExtensibleWithCustomName;
82+
composeManyExtensions makeExtensible makeExtensibleWithCustomName
83+
toExtension;
8384
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
8485
getAttrFromPath attrVals attrNames attrValues getAttrs catAttrs filterAttrs
8586
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs

lib/fixed-points.nix

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,4 +438,76 @@ rec {
438438
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
439439
}
440440
);
441+
442+
/**
443+
Convert to an extending function (overlay).
444+
445+
`toExtension` is the `toFunction` for extending functions (a.k.a. extensions or overlays).
446+
It converts a non-function or a single-argument function to an extending function,
447+
while returning a two-argument function as-is.
448+
449+
That is, it takes a value of the shape `x`, `prev: x`, or `final: prev: x`,
450+
and returns `final: prev: x`, assuming `x` is not a function.
451+
452+
This function takes care of the input to `stdenv.mkDerivation`'s
453+
`overrideAttrs` function.
454+
It bridges the gap between `<pkg>.overrideAttrs`
455+
before and after the overlay-style support.
456+
457+
# Inputs
458+
459+
`f`
460+
: The function or value to convert to an extending function.
461+
462+
# Type
463+
464+
```
465+
toExtension ::
466+
b' -> Any -> Any -> b'
467+
or
468+
toExtension ::
469+
(a -> b') -> Any -> a -> b'
470+
or
471+
toExtension ::
472+
(a -> a -> b) -> a -> a -> b
473+
where b' = ! Callable
474+
475+
Set a = b = b' = AttrSet & ! Callable to make toExtension return an extending function.
476+
```
477+
478+
# Examples
479+
:::{.example}
480+
## `lib.fixedPoints.toExtension` usage example
481+
482+
```nix
483+
fix (final: { a = 0; c = final.a; })
484+
=> { a = 0; c = 0; };
485+
486+
fix (extends (toExtension { a = 1; b = 2; }) (final: { a = 0; c = final.a; }))
487+
=> { a = 1; b = 2; c = 1; };
488+
489+
fix (extends (toExtension (prev: { a = 1; b = prev.a; })) (final: { a = 0; c = final.a; }))
490+
=> { a = 1; b = 0; c = 1; };
491+
492+
fix (extends (toExtension (final: prev: { a = 1; b = prev.a; c = final.a + 1 })) (final: { a = 0; c = final.a; }))
493+
=> { a = 1; b = 0; c = 2; };
494+
```
495+
:::
496+
*/
497+
toExtension =
498+
f:
499+
if lib.isFunction f then
500+
final: prev:
501+
let
502+
fPrev = f prev;
503+
in
504+
if lib.isFunction fPrev then
505+
# f is (final: prev: { ... })
506+
f final prev
507+
else
508+
# f is (prev: { ... })
509+
fPrev
510+
else
511+
# f is not a function; probably { ... }
512+
final: prev: f;
441513
}

lib/tests/misc.nix

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ let
4545
const
4646
escapeXML
4747
evalModules
48+
extends
4849
filter
4950
fix
5051
fold
@@ -102,6 +103,7 @@ let
102103
take
103104
testAllTrue
104105
toBaseDigits
106+
toExtension
105107
toHexString
106108
fromHexString
107109
toInt
@@ -233,11 +235,6 @@ runTests {
233235
];
234236
};
235237

236-
testFix = {
237-
expr = fix (x: {a = if x ? a then "a" else "b";});
238-
expected = {a = "a";};
239-
};
240-
241238
testComposeExtensions = {
242239
expr = let obj = makeExtensible (self: { foo = self.bar; });
243240
f = self: super: { bar = false; baz = true; };
@@ -1237,6 +1234,28 @@ runTests {
12371234
attrsToList { someFunc= a: a + 1;}
12381235
);
12391236

1237+
# FIXED-POINTS
1238+
1239+
testFix = {
1240+
expr = fix (x: {a = if x ? a then "a" else "b";});
1241+
expected = {a = "a";};
1242+
};
1243+
1244+
testToExtension = {
1245+
expr = [
1246+
(fix (final: { a = 0; c = final.a; }))
1247+
(fix (extends (toExtension { a = 1; b = 2; }) (final: { a = 0; c = final.a; })))
1248+
(fix (extends (toExtension (prev: { a = 1; b = prev.a; })) (final: { a = 0; c = final.a; })))
1249+
(fix (extends (toExtension (final: prev: { a = 1; b = prev.a; c = final.a + 1; })) (final: { a = 0; c = final.a; })))
1250+
];
1251+
expected = [
1252+
{ a = 0; c = 0; }
1253+
{ a = 1; b = 2; c = 1; }
1254+
{ a = 1; b = 0; c = 1; }
1255+
{ a = 1; b = 0; c = 2; }
1256+
];
1257+
};
1258+
12401259
# GENERATORS
12411260
# these tests assume attributes are converted to lists
12421261
# in alphabetical order

pkgs/build-support/go/module.nix

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,6 @@ let
6363
GO111MODULE = "on";
6464
GOTOOLCHAIN = "local";
6565

66-
toExtension =
67-
overlay0:
68-
if lib.isFunction overlay0 then
69-
final: prev:
70-
if lib.isFunction (overlay0 prev) then
71-
# `overlay0` is `final: prev: { ... }`
72-
overlay0 final prev
73-
else
74-
# `overlay0` is `prev: { ... }`
75-
overlay0 prev
76-
else
77-
# `overlay0` is `{ ... }`
78-
final: prev: overlay0;
79-
8066
in
8167
(stdenv.mkDerivation (finalAttrs:
8268
args
@@ -333,7 +319,7 @@ in
333319
# Canonicallize `overrideModAttrs` as an attribute overlay.
334320
# `passthru.overrideModAttrs` will be overridden
335321
# when users want to override `goModules`.
336-
overrideModAttrs = toExtension overrideModAttrs;
322+
overrideModAttrs = lib.toExtension overrideModAttrs;
337323
} // passthru;
338324

339325
meta = {

pkgs/stdenv/generic/make-derivation.nix

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,8 @@ let
6767
# ^^^^
6868

6969
overrideAttrs = f0:
70-
let
71-
f = self: super:
72-
# Convert f0 to an overlay. Legacy is:
73-
# overrideAttrs (super: {})
74-
# We want to introduce self. We follow the convention of overlays:
75-
# overrideAttrs (self: super: {})
76-
# Which means the first parameter can be either self or super.
77-
# This is surprising, but far better than the confusion that would
78-
# arise from flipping an overlay's parameters in some cases.
79-
let x = f0 super;
80-
in
81-
if builtins.isFunction x
82-
then
83-
# Can't reuse `x`, because `self` comes first.
84-
# Looks inefficient, but `f0 super` was a cheap thunk.
85-
f0 self super
86-
else x;
87-
in
88-
makeDerivationExtensible
89-
(self: let super = rattrs self; in super // (if builtins.isFunction f0 || f0?__functor then f self super else f0));
70+
makeDerivationExtensible
71+
(lib.extends (lib.toExtension f0) rattrs);
9072

9173
finalPackage =
9274
mkDerivationSimple overrideAttrs args;

0 commit comments

Comments
 (0)