Unverified Commit 20f9370d authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

lib.fixedPoints.toExtension: init (#336414)

parents 9a5abffb e31ace5c
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -79,7 +79,8 @@ let
      fromHexString toHexString toBaseDigits inPureEvalMode isBool isInt pathExists
      genericClosure readFile;
    inherit (self.fixedPoints) fix fix' converge extends composeExtensions
      composeManyExtensions makeExtensible makeExtensibleWithCustomName;
      composeManyExtensions makeExtensible makeExtensibleWithCustomName
      toExtension;
    inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
      getAttrFromPath attrVals attrNames attrValues getAttrs catAttrs filterAttrs
      filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
+72 −0
Original line number Diff line number Diff line
@@ -438,4 +438,76 @@ rec {
        ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
      }
    );

  /**
    Convert to an extending function (overlay).

    `toExtension` is the `toFunction` for extending functions (a.k.a. extensions or overlays).
    It converts a non-function or a single-argument function to an extending function,
    while returning a two-argument function as-is.

    That is, it takes a value of the shape `x`, `prev: x`, or `final: prev: x`,
    and returns `final: prev: x`, assuming `x` is not a function.

    This function takes care of the input to `stdenv.mkDerivation`'s
    `overrideAttrs` function.
    It bridges the gap between `<pkg>.overrideAttrs`
    before and after the overlay-style support.

    # Inputs

    `f`
    : The function or value to convert to an extending function.

    # Type

    ```
    toExtension ::
      b' -> Any -> Any -> b'
    or
    toExtension ::
      (a -> b') -> Any -> a -> b'
    or
    toExtension ::
      (a -> a -> b) -> a -> a -> b
    where b' = ! Callable

    Set a = b = b' = AttrSet & ! Callable to make toExtension return an extending function.
    ```

    # Examples
    :::{.example}
    ## `lib.fixedPoints.toExtension` usage example

    ```nix
    fix (final: { a = 0; c = final.a; })
    => { a = 0; c = 0; };

    fix (extends (toExtension { a = 1; b = 2; }) (final: { a = 0; c = final.a; }))
    => { a = 1; b = 2; c = 1; };

    fix (extends (toExtension (prev: { a = 1; b = prev.a; })) (final: { a = 0; c = final.a; }))
    => { a = 1; b = 0; c = 1; };

    fix (extends (toExtension (final: prev: { a = 1; b = prev.a; c = final.a + 1 })) (final: { a = 0; c = final.a; }))
    => { a = 1; b = 0; c = 2; };
    ```
    :::
  */
  toExtension =
    f:
    if lib.isFunction f then
      final: prev:
      let
        fPrev = f prev;
      in
      if lib.isFunction fPrev then
        # f is (final: prev: { ... })
        f final prev
      else
        # f is (prev: { ... })
        fPrev
    else
      # f is not a function; probably { ... }
      final: prev: f;
}
+24 −5
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ let
    const
    escapeXML
    evalModules
    extends
    filter
    fix
    fold
@@ -102,6 +103,7 @@ let
    take
    testAllTrue
    toBaseDigits
    toExtension
    toHexString
    fromHexString
    toInt
@@ -233,11 +235,6 @@ runTests {
    ];
  };

  testFix = {
    expr = fix (x: {a = if x ? a then "a" else "b";});
    expected = {a = "a";};
  };

  testComposeExtensions = {
    expr = let obj = makeExtensible (self: { foo = self.bar; });
               f = self: super: { bar = false; baz = true; };
@@ -1237,6 +1234,28 @@ runTests {
    attrsToList { someFunc= a: a + 1;}
  );

# FIXED-POINTS

  testFix = {
    expr = fix (x: {a = if x ? a then "a" else "b";});
    expected = {a = "a";};
  };

  testToExtension = {
    expr = [
      (fix (final: { a = 0; c = final.a; }))
      (fix (extends (toExtension { a = 1; b = 2; }) (final: { a = 0; c = final.a; })))
      (fix (extends (toExtension (prev: { a = 1; b = prev.a; })) (final: { a = 0; c = final.a; })))
      (fix (extends (toExtension (final: prev: { a = 1; b = prev.a; c = final.a + 1; })) (final: { a = 0; c = final.a; })))
    ];
    expected = [
      { a = 0; c = 0; }
      { a = 1; b = 2; c = 1; }
      { a = 1; b = 0; c = 1; }
      { a = 1; b = 0; c = 2; }
    ];
  };

# GENERATORS
# these tests assume attributes are converted to lists
# in alphabetical order
+1 −15
Original line number Diff line number Diff line
@@ -63,20 +63,6 @@ let
  GO111MODULE = "on";
  GOTOOLCHAIN = "local";

  toExtension =
    overlay0:
    if lib.isFunction overlay0 then
      final: prev:
      if lib.isFunction (overlay0 prev) then
        # `overlay0` is `final: prev: { ... }`
        overlay0 final prev
      else
        # `overlay0` is `prev: { ... }`
        overlay0 prev
    else
      # `overlay0` is `{ ... }`
      final: prev: overlay0;

in
(stdenv.mkDerivation (finalAttrs:
  args
@@ -333,7 +319,7 @@ in
      # Canonicallize `overrideModAttrs` as an attribute overlay.
      # `passthru.overrideModAttrs` will be overridden
      # when users want to override `goModules`.
      overrideModAttrs = toExtension overrideModAttrs;
      overrideModAttrs = lib.toExtension overrideModAttrs;
    } // passthru;

    meta = {
+2 −20
Original line number Diff line number Diff line
@@ -67,26 +67,8 @@ let
      #              ^^^^

      overrideAttrs = f0:
        let
          f = self: super:
            # Convert f0 to an overlay. Legacy is:
            #   overrideAttrs (super: {})
            # We want to introduce self. We follow the convention of overlays:
            #   overrideAttrs (self: super: {})
            # Which means the first parameter can be either self or super.
            # This is surprising, but far better than the confusion that would
            # arise from flipping an overlay's parameters in some cases.
            let x = f0 super;
            in
              if builtins.isFunction x
              then
                # Can't reuse `x`, because `self` comes first.
                # Looks inefficient, but `f0 super` was a cheap thunk.
                f0 self super
              else x;
        in
        makeDerivationExtensible
            (self: let super = rattrs self; in super // (if builtins.isFunction f0 || f0?__functor then f self super else f0));
          (lib.extends (lib.toExtension f0) rattrs);

      finalPackage =
        mkDerivationSimple overrideAttrs args;