nixpkgs/lib/trivial.nix
Profpatsch 8252861507 lib/trivial: add pipe function
`pipe` is a useful operator for creating pipelines of functions.

It works around the usual problem of e.g. string operations becoming
deeply nested functions.

In principle, there are four different ways this function could be
written:

pipe val [ f1 .. fn ]
pipe val [ fn .. f1 ]
compose [ f1 .. fn ] val
compose [ fn .. f1 ] val

The third and fourth form mirror composition of functions, they would
be the same as e.g. `(f1 << f2 << f3 .. << fn) val`.
However, it is not clear which direction the list should have (as one
can see in the second form, which is the most absurd.

In order not to confuse users, we decide for the most “intuitive”
form, which mirrors the way unix pipes work (thus the name `pipe`).
The flow of data goes from left to right.

Co-Authored-By: Silvan Mosberger <infinisil@icloud.com>
2019-10-21 13:19:16 +02:00

335 lines
8.6 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ lib }:
rec {
## Simple (higher order) functions
/* The identity function
For when you need a function that does nothing.
Type: id :: a -> a
*/
id =
# The value to return
x: x;
/* The constant function
Ignores the second argument. If called with only one argument,
constructs a function that always returns a static value.
Type: const :: a -> b -> a
Example:
let f = const 5; in f 10
=> 5
*/
const =
# Value to return
x:
# Value to ignore
y: x;
/* Pipes a value through a list of functions, left to right.
Type: pipe :: a -> [<functions>] -> <return type of last function>
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" {})
]
=> <drv which copies all files 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 dont 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, its obvious that it
goes first-to-last. With `compose`, not so much.
*/
## Named versions corresponding to some builtin operators.
/* Concatenate two lists
Type: concat :: [a] -> [a] -> [a]
Example:
concat [ 1 2 ] [ 3 4 ]
=> [ 1 2 3 4 ]
*/
concat = x: y: x ++ y;
/* boolean or */
or = x: y: x || y;
/* boolean and */
and = x: y: x && y;
/* bitwise and */
bitAnd = builtins.bitAnd
or (import ./zip-int-bits.nix
(a: b: if a==1 && b==1 then 1 else 0));
/* bitwise or */
bitOr = builtins.bitOr
or (import ./zip-int-bits.nix
(a: b: if a==1 || b==1 then 1 else 0));
/* bitwise xor */
bitXor = builtins.bitXor
or (import ./zip-int-bits.nix
(a: b: if a!=b then 1 else 0));
/* bitwise not */
bitNot = builtins.sub (-1);
/* Convert a boolean to a string.
This function uses the strings "true" and "false" to represent
boolean values. Calling `toString` on a bool instead returns "1"
and "" (sic!).
Type: boolToString :: bool -> string
*/
boolToString = b: if b then "true" else "false";
/* Merge two attribute sets shallowly, right side trumps left
mergeAttrs :: attrs -> attrs -> attrs
Example:
mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; }
=> { a = 1; b = 3; c = 4; }
*/
mergeAttrs =
# Left attribute set
x:
# Right attribute set (higher precedence for equal keys)
y: x // y;
/* Flip the order of the arguments of a binary function.
Type: flip :: (a -> b -> c) -> (b -> a -> c)
Example:
flip concat [1] [2]
=> [ 2 1 ]
*/
flip = f: a: b: f b a;
/* Apply function if the supplied argument is non-null.
Example:
mapNullable (x: x+1) null
=> null
mapNullable (x: x+1) 22
=> 23
*/
mapNullable =
# Function to call
f:
# Argument to check for null before passing it to `f`
a: if a == null then a else f a;
# Pull in some builtins not included elsewhere.
inherit (builtins)
pathExists readFile isBool
isInt isFloat add sub lessThan
seq deepSeq genericClosure;
## nixpks version strings
/* Returns the current full nixpkgs version number. */
version = release + versionSuffix;
/* Returns the current nixpkgs release number as string. */
release = lib.strings.fileContents ../.version;
/* Returns the current nixpkgs release code name.
On each release the first letter is bumped and a new animal is chosen
starting with that new letter.
*/
codeName = "Markhor";
/* Returns the current nixpkgs version suffix as string. */
versionSuffix =
let suffixFile = ../.version-suffix;
in if pathExists suffixFile
then lib.strings.fileContents suffixFile
else "pre-git";
/* Attempts to return the the current revision of nixpkgs and
returns the supplied default value otherwise.
Type: revisionWithDefault :: string -> string
*/
revisionWithDefault =
# Default value to return if revision can not be determined
default:
let
revisionFile = "${toString ./..}/.git-revision";
gitRepo = "${toString ./..}/.git";
in if lib.pathIsDirectory gitRepo
then lib.commitIdFromGitRepo gitRepo
else if lib.pathExists revisionFile then lib.fileContents revisionFile
else default;
nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version;
/* Determine whether the function is being called from inside a Nix
shell.
Type: inNixShell :: bool
*/
inNixShell = builtins.getEnv "IN_NIX_SHELL" != "";
## Integer operations
/* Return minimum of two numbers. */
min = x: y: if x < y then x else y;
/* Return maximum of two numbers. */
max = x: y: if x > y then x else y;
/* Integer modulus
Example:
mod 11 10
=> 1
mod 1 10
=> 1
*/
mod = base: int: base - (int * (builtins.div base int));
## Comparisons
/* C-style comparisons
a < b, compare a b => -1
a == b, compare a b => 0
a > b, compare a b => 1
*/
compare = a: b:
if a < b
then -1
else if a > b
then 1
else 0;
/* Split type into two subtypes by predicate `p`, take all elements
of the first subtype to be less than all the elements of the
second subtype, compare elements of a single subtype with `yes`
and `no` respectively.
Type: (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int)
Example:
let cmp = splitByAndCompare (hasPrefix "foo") compare compare; in
cmp "a" "z" => -1
cmp "fooa" "fooz" => -1
cmp "f" "a" => 1
cmp "fooa" "a" => -1
# while
compare "fooa" "a" => 1
*/
splitByAndCompare =
# Predicate
p:
# Comparison function if predicate holds for both values
yes:
# Comparison function if predicate holds for neither value
no:
# First value to compare
a:
# Second value to compare
b:
if p a
then if p b then yes a b else -1
else if p b then 1 else no a b;
/* Reads a JSON file.
Type :: path -> any
*/
importJSON = path:
builtins.fromJSON (builtins.readFile path);
## Warnings
# See https://github.com/NixOS/nix/issues/749. Eventually we'd like these
# to expand to Nix builtins that carry metadata so that Nix can filter out
# the INFO messages without parsing the message string.
#
# Usage:
# {
# foo = lib.warn "foo is deprecated" oldFoo;
# }
#
# TODO: figure out a clever way to integrate location information from
# something like __unsafeGetAttrPos.
warn = msg: builtins.trace "warning: ${msg}";
info = msg: builtins.trace "INFO: ${msg}";
showWarnings = warnings: res: lib.fold (w: x: warn w x) res warnings;
## Function annotations
/* Add metadata about expected function arguments to a function.
The metadata should match the format given by
builtins.functionArgs, i.e. a set from expected argument to a bool
representing whether that argument has a default or not.
setFunctionArgs : (a b) Map String Bool (a b)
This function is necessary because you can't dynamically create a
function of the { a, b ? foo, ... }: format, but some facilities
like callPackage expect to be able to query expected arguments.
*/
setFunctionArgs = f: args:
{ # TODO: Should we add call-time "type" checking like built in?
__functor = self: f;
__functionArgs = args;
};
/* Extract the expected function arguments from a function.
This works both with nix-native { a, b ? foo, ... }: style
functions and functions with args set with 'setFunctionArgs'. It
has the same return type and semantics as builtins.functionArgs.
setFunctionArgs : (a b) Map String Bool.
*/
functionArgs = f: f.__functionArgs or (builtins.functionArgs f);
/* Check whether something is a function or something
annotated with function args.
*/
isFunction = f: builtins.isFunction f ||
(f ? __functor && isFunction (f.__functor f));
}