diff --git a/lib/default.nix b/lib/default.nix index f293a1defb1..74d508ec1d6 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -57,8 +57,8 @@ let hasAttr head isAttrs isBool isInt isList isString length lessThan listToAttrs pathExists readFile replaceStrings seq stringLength sub substring tail; - inherit (trivial) id const concat or and bitAnd bitOr bitXor bitNot - boolToString mergeAttrs flip mapNullable inNixShell min max + inherit (trivial) id const pipe concat or and bitAnd bitOr bitXor + bitNot boolToString mergeAttrs flip mapNullable inNixShell min max importJSON warn info showWarnings nixpkgsVersion version mod compare splitByAndCompare functionArgs setFunctionArgs isFunction; inherit (fixedPoints) fix fix' converge extends composeExtensions diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index e5d76d4e57b..b064faa1e1b 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -18,6 +18,31 @@ runTests { expected = 2; }; + testPipe = { + expr = pipe 2 [ + (x: x + 2) # 2 + 2 = 4 + (x: x * 2) # 4 * 2 = 8 + ]; + expected = 8; + }; + + testPipeEmpty = { + expr = pipe 2 []; + expected = 2; + }; + + testPipeStrings = { + expr = pipe [ 3 4 ] [ + (map toString) + (map (s: s + "\n")) + concatStrings + ]; + expected = '' + 3 + 4 + ''; + }; + /* testOr = { expr = or true false; diff --git a/lib/trivial.nix b/lib/trivial.nix index 54c66cfce7b..3a25e31fb05 100644 --- a/lib/trivial.nix +++ b/lib/trivial.nix @@ -29,6 +29,43 @@ rec { # Value to ignore y: x; + /* Pipes a value through a list of functions, left to right. + + Type: pipe :: a -> [] -> + Example: + pipe 2 [ + (x: x + 2) # 2 + 2 = 4 + (x: x * 2) # 4 * 2 = 8 + ] + => 8 + + # ideal to do text transformations + pipe [ "a/b" "a/c" ] [ + + # create the cp command + (map (file: ''cp "${src}/${file}" $out\n'')) + + # concatenate all commands into one string + lib.concatStrings + + # make that string into a nix derivation + (pkgs.runCommand "copy-to-out" {}) + + ] + => + + The output type of each function has to be the input type + of the next function, and the last function returns the + final value. + */ + pipe = val: functions: + let reverseApply = x: f: f x; + in builtins.foldl' reverseApply val functions; + /* note please don’t add a function like `compose = flip pipe`. + This would confuse users, because the order of the functions + in the list is not clear. With pipe, it’s obvious that it + goes first-to-last. With `compose`, not so much. + */ ## Named versions corresponding to some builtin operators.