2024-05-01 22:14:04 +00:00
/* *
Operations on attribute sets .
* /
{ lib }:
let
inherit ( builtins ) head length ;
inherit ( lib . trivial )
isInOldestRelease
mergeAttrs
warn
warnIf
;
inherit ( lib . strings )
concatStringsSep
concatMapStringsSep
escapeNixIdentifier
sanitizeDerivationName
;
inherit ( lib . lists )
foldr
foldl'
concatMap
elemAt
all
partition
groupBy
take
foldl
;
in
rec {
inherit ( builtins )
attrNames
listToAttrs
hasAttr
isAttrs
getAttr
removeAttrs
;
/* *
Return an attribute from nested attribute sets .
Nix has an [ attribute selection operator ` . or ` ] ( https://nixos.org/manual/nix/stable/language/operators #attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
( x . a . b or 6 ) == attrByPath [ " a " " b " ] 6 x
# and
( x . ${ f p } . " e x a m p l e . c o m " or 6 ) == attrByPath [ ( f p ) " e x a m p l e . c o m " ] 6 x
` ` `
# Inputs
` attrPath `
: A list of strings representing the attribute path to return from ` set `
` default `
: Default value if ` attrPath ` does not resolve to an existing value
` set `
: The nested attribute set to select values from
# Type
` ` `
attrByPath : : [ String ] -> Any -> AttrSet -> Any
` ` `
# Examples
: : : { . example }
## `lib.attrsets.attrByPath` usage example
` ` ` nix
x = { a = { b = 3 ; } ; }
# ["a" "b"] is equivalent to x.a.b
# 6 is a default value to return if the path does not exist in attrset
attrByPath [ " a " " b " ] 6 x
= > 3
attrByPath [ " z " " z " ] 6 x
= > 6
` ` `
: : :
* /
attrByPath =
attrPath : default : set :
let
lenAttrPath = length attrPath ;
attrByPath' =
n : s :
(
if n == lenAttrPath then
s
else
(
let
attr = elemAt attrPath n ;
in
if s ? ${ attr } then attrByPath' ( n + 1 ) s . ${ attr } else default
)
) ;
in
attrByPath' 0 set ;
/* *
Return if an attribute from nested attribute set exists .
Nix has a [ has attribute operator ` ? ` ] ( https://nixos.org/manual/nix/stable/language/operators #has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
( x ? a . b ) == hasAttrByPath [ " a " " b " ] x
# and
( x ? ${ f p } . " e x a m p l e . c o m " ) == hasAttrByPath [ ( f p ) " e x a m p l e . c o m " ] x
` ` `
* * Laws * * :
1 . ` ` ` nix
hasAttrByPath [ ] x == true
` ` `
# Inputs
` attrPath `
: A list of strings representing the attribute path to check from ` set `
` e `
: The nested attribute set to check
# Type
` ` `
hasAttrByPath : : [ String ] -> AttrSet -> Bool
` ` `
# Examples
: : : { . example }
## `lib.attrsets.hasAttrByPath` usage example
` ` ` nix
x = { a = { b = 3 ; } ; }
hasAttrByPath [ " a " " b " ] x
= > true
hasAttrByPath [ " z " " z " ] x
= > false
hasAttrByPath [ ] ( throw " n o n e e d " )
= > true
` ` `
: : :
* /
hasAttrByPath =
attrPath : e :
let
lenAttrPath = length attrPath ;
hasAttrByPath' =
n : s :
(
n == lenAttrPath
|| (
let
attr = elemAt attrPath n ;
in
if s ? ${ attr } then hasAttrByPath' ( n + 1 ) s . ${ attr } else false
)
) ;
in
hasAttrByPath' 0 e ;
/* *
Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets .
Can be used after [ ` mapAttrsRecursiveCond ` ] ( #function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition,
although this will evaluate the predicate function on sibling attributes as well .
Note that the empty attribute path is valid for all values , so this function only throws an exception if any of its inputs does .
* * Laws * * :
1 . ` ` ` nix
attrsets . longestValidPathPrefix [ ] x == [ ]
` ` `
2 . ` ` ` nix
hasAttrByPath ( attrsets . longestValidPathPrefix p x ) x == true
` ` `
# Inputs
` attrPath `
: A list of strings representing the longest possible path that may be returned .
` v `
: The nested attribute set to check .
# Type
` ` `
attrsets . longestValidPathPrefix : : [ String ] -> Value -> [ String ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.longestValidPathPrefix` usage example
` ` ` nix
x = { a = { b = 3 ; } ; }
attrsets . longestValidPathPrefix [ " a " " b " " c " ] x
= > [ " a " " b " ]
attrsets . longestValidPathPrefix [ " a " ] x
= > [ " a " ]
attrsets . longestValidPathPrefix [ " z " " z " ] x
= > [ ]
attrsets . longestValidPathPrefix [ " z " " z " ] ( throw " n o n e e d " )
= > [ ]
` ` `
: : :
* /
longestValidPathPrefix =
attrPath : v :
let
lenAttrPath = length attrPath ;
getPrefixForSetAtIndex =
# The nested attribute set to check, if it is an attribute set, which
# is not a given.
remainingSet :
# The index of the attribute we're about to check, as well as
# the length of the prefix we've already checked.
remainingPathIndex :
if remainingPathIndex == lenAttrPath then
# All previously checked attributes exist, and no attr names left,
# so we return the whole path.
2024-06-30 08:12:46 +00:00
attrPath
else
let
2024-05-01 22:14:04 +00:00
attr = elemAt attrPath remainingPathIndex ;
2024-06-30 08:12:46 +00:00
in
2024-05-01 22:14:04 +00:00
if remainingSet ? ${ attr } then
getPrefixForSetAtIndex remainingSet . ${ attr } # advance from the set to the attribute value
( remainingPathIndex + 1 ) # advance the path
else
# The attribute doesn't exist, so we return the prefix up to the
# previously checked length.
take remainingPathIndex attrPath ;
in
getPrefixForSetAtIndex v 0 ;
/* *
Create a new attribute set with ` value ` set at the nested attribute location specified in ` attrPath ` .
# Inputs
` attrPath `
: A list of strings representing the attribute path to set
` value `
: The value to set at the location described by ` attrPath `
# Type
` ` `
setAttrByPath : : [ String ] -> Any -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.setAttrByPath` usage example
` ` ` nix
setAttrByPath [ " a " " b " ] 3
= > { a = { b = 3 ; } ; }
` ` `
: : :
* /
setAttrByPath =
attrPath : value :
let
len = length attrPath ;
atDepth = n : if n == len then value else { ${ elemAt attrPath n } = atDepth ( n + 1 ) ; } ;
in
atDepth 0 ;
/* *
Like ` attrByPath ` , but without a default value . If it doesn't find the
path it will throw an error .
Nix has an [ attribute selection operator ] ( https://nixos.org/manual/nix/stable/language/operators #attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
x . a . b == getAttrByPath [ " a " " b " ] x
# and
x . ${ f p } . " e x a m p l e . c o m " == getAttrByPath [ ( f p ) " e x a m p l e . c o m " ] x
` ` `
# Inputs
` attrPath `
: A list of strings representing the attribute path to get from ` set `
` set `
: The nested attribute set to find the value in .
# Type
` ` `
getAttrFromPath : : [ String ] -> AttrSet -> Any
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getAttrFromPath` usage example
` ` ` nix
x = { a = { b = 3 ; } ; }
getAttrFromPath [ " a " " b " ] x
= > 3
getAttrFromPath [ " z " " z " ] x
= > error : cannot find attribute ` z . z'
` ` `
: : :
* /
getAttrFromPath =
attrPath : set :
attrByPath attrPath ( abort ( " c a n n o t f i n d a t t r i b u t e ` " + concatStringsSep " . " attrPath + " ' " ) ) set ;
/* *
Map each attribute in the given set and merge them into a new attribute set .
# Inputs
` f `
: 1 \ . Function argument
` v `
: 2 \ . Function argument
# Type
` ` `
concatMapAttrs : : ( String -> a -> AttrSet ) -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.concatMapAttrs` usage example
` ` ` nix
concatMapAttrs
( name : value : {
$ { name } = value ;
$ { name + value } = value ;
} )
{ x = " a " ; y = " b " ; }
= > { x = " a " ; xa = " a " ; y = " b " ; yb = " b " ; }
` ` `
: : :
* /
concatMapAttrs = f : v : foldl' mergeAttrs { } ( attrValues ( mapAttrs f v ) ) ;
/* *
Update or set specific paths of an attribute set .
Takes a list of updates to apply and an attribute set to apply them to ,
and returns the attribute set with the updates applied . Updates are
represented as ` { path = . . . ; update = . . . ; } ` values , where ` path ` is a
list of strings representing the attribute path that should be updated ,
and ` update ` is a function that takes the old value at that attribute path
as an argument and returns the new
value it should be .
Properties :
- Updates to deeper attribute paths are applied before updates to more
shallow attribute paths
- Multiple updates to the same attribute path are applied in the order
they appear in the update list
- If any but the last ` path ` element leads into a value that is not an
attribute set , an error is thrown
- If there is an update for an attribute path that doesn't exist ,
accessing the argument in the update function causes an error , but
intermediate attribute sets are implicitly created as needed
# Type
` ` `
updateManyAttrsByPath : : [ { path : : [ String ] ; update : : ( Any -> Any ) ; } ] -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.updateManyAttrsByPath` usage example
` ` ` nix
updateManyAttrsByPath [
{
path = [ " a " " b " ] ;
update = old : { d = old . c ; } ;
}
{
path = [ " a " " b " " c " ] ;
update = old : old + 1 ;
}
{
path = [ " x " " y " ] ;
update = old : " x y " ;
}
] { a . b . c = 0 ; }
= > { a = { b = { d = 1 ; } ; } ; x = { y = " x y " ; } ; }
` ` `
: : :
* /
updateManyAttrsByPath =
let
# When recursing into attributes, instead of updating the `path` of each
# update using `tail`, which needs to allocate an entirely new list,
# we just pass a prefix length to use and make sure to only look at the
# path without the prefix length, so that we can reuse the original list
# entries.
go =
prefixLength : hasValue : value : updates :
let
# Splits updates into ones on this level (split.right)
# And ones on levels further down (split.wrong)
split = partition ( el : length el . path == prefixLength ) updates ;
2024-06-30 08:12:46 +00:00
2024-05-01 22:14:04 +00:00
# Groups updates on further down levels into the attributes they modify
nested = groupBy ( el : elemAt el . path prefixLength ) split . wrong ;
2024-06-30 08:12:46 +00:00
2024-05-01 22:14:04 +00:00
# Applies only nested modification to the input value
withNestedMods =
# Return the value directly if we don't have any nested modifications
if split . wrong == [ ] then
if hasValue then
2024-06-30 08:12:46 +00:00
value
else
2024-05-01 22:14:04 +00:00
# Throw an error if there is no value. This `head` call here is
# safe, but only in this branch since `go` could only be called
# with `hasValue == false` for nested updates, in which case
# it's also always called with at least one update
2024-06-30 08:12:46 +00:00
let
2024-05-01 22:14:04 +00:00
updatePath = ( head split . right ) . path ;
2024-06-30 08:12:46 +00:00
in
throw (
2024-05-01 22:14:04 +00:00
" u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' d o e s "
+ " n o t e x i s t i n t h e g i v e n v a l u e , b u t t h e f i r s t u p d a t e t o t h i s "
+ " p a t h t r i e s t o a c c e s s t h e e x i s t i n g v a l u e . "
2024-06-30 08:12:46 +00:00
)
2024-05-01 22:14:04 +00:00
else
# If there are nested modifications, try to apply them to the value
if ! hasValue then
# But if we don't have a value, just use an empty attribute set
# as the value, but simplify the code a bit
mapAttrs ( name : go ( prefixLength + 1 ) false null ) nested
else if isAttrs value then
# If we do have a value and it's an attribute set, override it
# with the nested modifications
value // mapAttrs ( name : go ( prefixLength + 1 ) ( value ? ${ name } ) value . ${ name } ) nested
else
# However if it's not an attribute set, we can't apply the nested
# modifications, throw an error
let
updatePath = ( head split . wrong ) . path ;
in
throw (
" u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' n e e d s t o "
+ " b e u p d a t e d , b u t p a t h ' ${ showAttrPath ( take prefixLength updatePath ) } ' "
+ " o f t h e g i v e n v a l u e i s n o t a n a t t r i b u t e s e t , s o w e c a n ' t "
+ " u p d a t e a n a t t r i b u t e i n s i d e o f i t . "
) ;
2024-06-30 08:12:46 +00:00
in
2024-05-01 22:14:04 +00:00
# We get the final result by applying all the updates on this level
# after having applied all the nested updates
# We use foldl instead of foldl' so that in case of multiple updates,
# intermediate values aren't evaluated if not needed
foldl ( acc : el : el . update acc ) withNestedMods split . right ;
in
updates : value : go 0 true value updates ;
/* *
Return the specified attributes from a set .
# Inputs
` nameList `
: The list of attributes to fetch from ` set ` . Each attribute name must exist on the attrbitue set
` set `
: The set to get attribute values from
# Type
` ` `
attrVals : : [ String ] -> AttrSet -> [ Any ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.attrVals` usage example
` ` ` nix
attrVals [ " a " " b " " c " ] as
= > [ as . a as . b as . c ]
` ` `
: : :
* /
attrVals = nameList : set : map ( x : set . ${ x } ) nameList ;
/* *
Return the values of all attributes in the given set , sorted by
attribute name .
# Type
` ` `
attrValues : : AttrSet -> [ Any ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.attrValues` usage example
` ` ` nix
attrValues { c = 3 ; a = 1 ; b = 2 ; }
= > [ 1 2 3 ]
` ` `
: : :
* /
attrValues = builtins . attrValues ;
/* *
Given a set of attribute names , return the set of the corresponding
attributes from the given set .
# Inputs
` names `
: A list of attribute names to get out of ` set `
` attrs `
: The set to get the named attributes from
# Type
` ` `
getAttrs : : [ String ] -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getAttrs` usage example
` ` ` nix
getAttrs [ " a " " b " ] { a = 1 ; b = 2 ; c = 3 ; }
= > { a = 1 ; b = 2 ; }
` ` `
: : :
* /
getAttrs = names : attrs : genAttrs names ( name : attrs . ${ name } ) ;
/* *
Collect each attribute named ` attr ` from a list of attribute
sets . Sets that don't contain the named attribute are ignored .
# Inputs
` attr `
: The attribute name to get out of the sets .
` list `
: The list of attribute sets to go through
# Type
` ` `
catAttrs : : String -> [ AttrSet ] -> [ Any ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.catAttrs` usage example
` ` ` nix
catAttrs " a " [ { a = 1 ; } { b = 0 ; } { a = 2 ; } ]
= > [ 1 2 ]
` ` `
: : :
* /
catAttrs = builtins . catAttrs ;
/* *
Filter an attribute set by removing all attributes for which the
given predicate return false .
# Inputs
` pred `
: Predicate taking an attribute name and an attribute value , which returns ` true ` to include the attribute , or ` false ` to exclude the attribute .
` set `
: The attribute set to filter
# Type
` ` `
filterAttrs : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.filterAttrs` usage example
` ` ` nix
filterAttrs ( n : v : n == " f o o " ) { foo = 1 ; bar = 2 ; }
= > { foo = 1 ; }
` ` `
: : :
* /
filterAttrs =
pred : set :
listToAttrs (
concatMap (
name :
let
v = set . ${ name } ;
in
if pred name v then [ ( nameValuePair name v ) ] else [ ]
) ( attrNames set )
) ;
/* *
Filter an attribute set recursively by removing all attributes for
which the given predicate return false .
# Inputs
` pred `
: Predicate taking an attribute name and an attribute value , which returns ` true ` to include the attribute , or ` false ` to exclude the attribute .
` set `
: The attribute set to filter
# Type
` ` `
filterAttrsRecursive : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.filterAttrsRecursive` usage example
` ` ` nix
filterAttrsRecursive ( n : v : v != null ) { foo = { bar = null ; } ; }
= > { foo = { } ; }
` ` `
: : :
* /
filterAttrsRecursive =
pred : set :
listToAttrs (
concatMap (
name :
let
v = set . ${ name } ;
in
if pred name v then
[ ( nameValuePair name ( if isAttrs v then filterAttrsRecursive pred v else v ) ) ]
else
[ ]
) ( attrNames set )
) ;
/* *
Like [ ` lib . lists . foldl' ` ] ( #function-library-lib.lists.foldl-prime) but for attribute sets.
Iterates over every name-value pair in the given attribute set .
The result of the callback function is often called ` acc ` for accumulator . It is passed between callbacks from left to right and the final ` acc ` is the return value of ` foldlAttrs ` .
Attention :
There is a completely different function ` lib . foldAttrs `
which has nothing to do with this function , despite the similar name .
# Inputs
` f `
: 1 \ . Function argument
` init `
: 2 \ . Function argument
` set `
: 3 \ . Function argument
# Type
` ` `
foldlAttrs : : ( a -> String -> b -> a ) -> a -> { . . . : : b } -> a
` ` `
# Examples
: : : { . example }
## `lib.attrsets.foldlAttrs` usage example
` ` ` nix
foldlAttrs
( acc : name : value : {
sum = acc . sum + value ;
names = acc . names ++ [ name ] ;
} )
{ sum = 0 ; names = [ ] ; }
{
foo = 1 ;
bar = 10 ;
}
->
{
sum = 11 ;
names = [ " b a r " " f o o " ] ;
}
foldlAttrs
( throw " f u n c t i o n n o t n e e d e d " )
123
{ } ;
->
123
foldlAttrs
( acc : _ : _ : acc )
3
{ z = throw " v a l u e n o t n e e d e d " ; a = throw " v a l u e n o t n e e d e d " ; } ;
->
3
The accumulator doesn't have to be an attrset .
It can be as simple as a number or string .
foldlAttrs
( acc : _ : v : acc * 10 + v )
1
{ z = 1 ; a = 2 ; } ;
->
121
` ` `
: : :
* /
foldlAttrs =
f : init : set :
foldl' ( acc : name : f acc name set . ${ name } ) init ( attrNames set ) ;
/* *
Apply fold functions to values grouped by key .
# Inputs
` op `
: A function , given a value and a collector combines the two .
` nul `
: The starting value .
` list_of_attrs `
: A list of attribute sets to fold together by key .
# Type
` ` `
foldAttrs : : ( Any -> Any -> Any ) -> Any -> [ AttrSets ] -> Any
` ` `
# Examples
: : : { . example }
## `lib.attrsets.foldAttrs` usage example
` ` ` nix
foldAttrs ( item : acc : [ item ] ++ acc ) [ ] [ { a = 2 ; } { a = 3 ; } ]
= > { a = [ 2 3 ] ; }
` ` `
: : :
* /
foldAttrs =
op : nul : list_of_attrs :
foldr (
n : a : foldr ( name : o : o // { ${ name } = op n . ${ name } ( a . ${ name } or nul ) ; } ) a ( attrNames n )
) { } list_of_attrs ;
/* *
Recursively collect sets that verify a given predicate named ` pred `
from the set ` attrs ` . The recursion is stopped when the predicate is
verified .
# Inputs
` pred `
: Given an attribute's value , determine if recursion should stop .
` attrs `
: The attribute set to recursively collect .
# Type
` ` `
collect : : ( AttrSet -> Bool ) -> AttrSet -> [ x ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.collect` usage example
` ` ` nix
collect isList { a = { b = [ " b " ] ; } ; c = [ 1 ] ; }
= > [ [ " b " ] [ 1 ] ]
collect ( x : x ? outPath )
{ a = { outPath = " a / " ; } ; b = { outPath = " b / " ; } ; }
= > [ { outPath = " a / " ; } { outPath = " b / " ; } ]
` ` `
: : :
* /
collect =
pred : attrs :
if pred attrs then
[ attrs ]
else if isAttrs attrs then
concatMap ( collect pred ) ( attrValues attrs )
else
[ ] ;
/* *
Return the cartesian product of attribute set value combinations .
# Inputs
` attrsOfLists `
: Attribute set with attributes that are lists of values
# Type
` ` `
cartesianProduct : : AttrSet -> [ AttrSet ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.cartesianProduct` usage example
` ` ` nix
cartesianProduct { a = [ 1 2 ] ; b = [ 10 20 ] ; }
= > [
{ a = 1 ; b = 10 ; }
{ a = 1 ; b = 20 ; }
{ a = 2 ; b = 10 ; }
{ a = 2 ; b = 20 ; }
]
` ` `
: : :
* /
cartesianProduct =
attrsOfLists :
foldl' (
listOfAttrs : attrName :
concatMap (
attrs : map ( listValue : attrs // { ${ attrName } = listValue ; } ) attrsOfLists . ${ attrName }
) listOfAttrs
) [ { } ] ( attrNames attrsOfLists ) ;
/* *
Return the result of function f applied to the cartesian product of attribute set value combinations .
Equivalent to using cartesianProduct followed by map .
# Inputs
` f `
: A function , given an attribute set , it returns a new value .
` attrsOfLists `
: Attribute set with attributes that are lists of values
# Type
` ` `
mapCartesianProduct : : ( AttrSet -> a ) -> AttrSet -> [ a ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.mapCartesianProduct` usage example
` ` ` nix
mapCartesianProduct ( { a , b }: " ${ a } - ${ b } " ) { a = [ " 1 " " 2 " ] ; b = [ " 3 " " 4 " ] ; }
= > [ " 1 - 3 " " 1 - 4 " " 2 - 3 " " 2 - 4 " ]
` ` `
: : :
* /
mapCartesianProduct = f : attrsOfLists : map f ( cartesianProduct attrsOfLists ) ;
/* *
Utility function that creates a ` { name , value } ` pair as expected by ` builtins . listToAttrs ` .
# Inputs
` name `
: Attribute name
` value `
: Attribute value
# Type
` ` `
nameValuePair : : String -> Any -> { name : : String ; value : : Any ; }
` ` `
# Examples
: : : { . example }
## `lib.attrsets.nameValuePair` usage example
` ` ` nix
nameValuePair " s o m e " 6
= > { name = " s o m e " ; value = 6 ; }
` ` `
: : :
* /
nameValuePair = name : value : { inherit name value ; } ;
/* *
Apply a function to each element in an attribute set , creating a new attribute set .
# Inputs
` f `
: A function that takes an attribute name and its value , and returns the new value for the attribute .
` attrset `
: The attribute set to iterate through .
# Type
` ` `
mapAttrs : : ( String -> Any -> Any ) -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.mapAttrs` usage example
` ` ` nix
mapAttrs ( name : value : name + " - " + value )
{ x = " f o o " ; y = " b a r " ; }
= > { x = " x - f o o " ; y = " y - b a r " ; }
` ` `
: : :
* /
mapAttrs = builtins . mapAttrs ;
/* *
Like ` mapAttrs ` , but allows the name of each attribute to be
changed in addition to the value . The applied function should
return both the new name and value as a ` nameValuePair ` .
# Inputs
` f `
: A function , given an attribute's name and value , returns a new ` nameValuePair ` .
` set `
: Attribute set to map over .
# Type
` ` `
mapAttrs' : : ( String -> Any -> { name : : String ; value : : Any ; } ) -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.mapAttrs'` usage example
` ` ` nix
mapAttrs' ( name : value : nameValuePair ( " f o o _ " + name ) ( " b a r - " + value ) )
{ x = " a " ; y = " b " ; }
= > { foo_x = " b a r - a " ; foo_y = " b a r - b " ; }
` ` `
: : :
* /
mapAttrs' = f : set : listToAttrs ( map ( attr : f attr set . ${ attr } ) ( attrNames set ) ) ;
/* *
Call a function for each attribute in the given set and return
the result in a list .
# Inputs
` f `
: A function , given an attribute's name and value , returns a new value .
` attrs `
: Attribute set to map over .
# Type
` ` `
mapAttrsToList : : ( String -> a -> b ) -> AttrSet -> [ b ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.mapAttrsToList` usage example
` ` ` nix
mapAttrsToList ( name : value : name + value )
{ x = " a " ; y = " b " ; }
= > [ " x a " " y b " ]
` ` `
: : :
* /
mapAttrsToList = f : attrs : map ( name : f name attrs . ${ name } ) ( attrNames attrs ) ;
/* *
Deconstruct an attrset to a list of name-value pairs as expected by [ ` builtins . listToAttrs ` ] ( https://nixos.org/manual/nix/stable/language/builtins.html #builtins-listToAttrs).
Each element of the resulting list is an attribute set with these attributes :
- ` name ` ( string ) : The name of the attribute
- ` value ` ( any ) : The value of the attribute
The following is always true :
` ` ` nix
builtins . listToAttrs ( attrsToList attrs ) == attrs
` ` `
: : : { . warning }
The opposite is not always true . In general expect that
` ` ` nix
attrsToList ( builtins . listToAttrs list ) != list
` ` `
This is because the ` listToAttrs ` removes duplicate names and doesn't preserve the order of the list .
: : :
# Inputs
` set `
: The attribute set to deconstruct .
# Type
` ` `
attrsToList : : AttrSet -> [ { name : : String ; value : : Any ; } ]
` ` `
# Examples
: : : { . example }
## `lib.attrsets.attrsToList` usage example
` ` ` nix
attrsToList { foo = 1 ; bar = " a s d f " ; }
= > [ { name = " b a r " ; value = " a s d f " ; } { name = " f o o " ; value = 1 ; } ]
` ` `
: : :
* /
attrsToList = mapAttrsToList nameValuePair ;
/* *
Like ` mapAttrs ` , except that it recursively applies itself to the * leaf * attributes of a potentially-nested attribute set :
the second argument of the function will never be an attrset .
Also , the first argument of the mapping function is a * list * of the attribute names that form the path to the leaf attribute .
For a function that gives you control over what counts as a leaf , see ` mapAttrsRecursiveCond ` .
: : : { #map-attrs-recursive-example .example}
# Map over leaf attributes
` ` ` nix
mapAttrsRecursive ( path : value : concatStringsSep " - " ( path ++ [ value ] ) )
{ n = { a = " A " ; m = { b = " B " ; c = " C " ; } ; } ; d = " D " ; }
` ` `
evaluates to
` ` ` nix
{ n = { a = " n - a - A " ; m = { b = " n - m - b - B " ; c = " n - m - c - C " ; } ; } ; d = " d - D " ; }
` ` `
: : :
# Type
` ` `
mapAttrsRecursive : : ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
` ` `
* /
mapAttrsRecursive = f : set : mapAttrsRecursiveCond ( as : true ) f set ;
/* *
Like ` mapAttrsRecursive ` , but it takes an additional predicate that tells it whether to recurse into an attribute set .
If the predicate returns false , ` mapAttrsRecursiveCond ` does not recurse , but instead applies the mapping function .
If the predicate returns true , it does recurse , and does not apply the mapping function .
: : : { #map-attrs-recursive-cond-example .example}
# Map over an leaf attributes defined by a condition
Map derivations to their ` name ` attribute .
Derivatons are identified as attribute sets that contain ` { type = " d e r i v a t i o n " ; } ` .
` ` ` nix
mapAttrsRecursiveCond
( as : ! ( as ? " t y p e " && as . type == " d e r i v a t i o n " ) )
( x : x . name )
attrs
` ` `
: : :
# Type
` ` `
mapAttrsRecursiveCond : : ( AttrSet -> Bool ) -> ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
` ` `
* /
mapAttrsRecursiveCond =
cond : f : set :
let
recurse =
path :
mapAttrs (
name : value :
if isAttrs value && cond value then recurse ( path ++ [ name ] ) value else f ( path ++ [ name ] ) value
) ;
in
recurse [ ] set ;
/* *
Generate an attribute set by mapping a function over a list of
attribute names .
# Inputs
` names `
: Names of values in the resulting attribute set .
` f `
: A function , given the name of the attribute , returns the attribute's value .
# Type
` ` `
genAttrs : : [ String ] -> ( String -> Any ) -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.genAttrs` usage example
` ` ` nix
genAttrs [ " f o o " " b a r " ] ( name : " x _ " + name )
= > { foo = " x _ f o o " ; bar = " x _ b a r " ; }
` ` `
: : :
* /
genAttrs = names : f : listToAttrs ( map ( n : nameValuePair n ( f n ) ) names ) ;
/* *
Check whether the argument is a derivation . Any set with
` { type = " d e r i v a t i o n " ; } ` counts as a derivation .
# Inputs
` value `
: Value to check .
# Type
` ` `
isDerivation : : Any -> Bool
` ` `
# Examples
: : : { . example }
## `lib.attrsets.isDerivation` usage example
` ` ` nix
nixpkgs = import <nixpkgs> { }
isDerivation nixpkgs . ruby
= > true
isDerivation " f o o b a r "
= > false
` ` `
: : :
* /
isDerivation = value : value . type or null == " d e r i v a t i o n " ;
/* *
Converts a store path to a fake derivation .
# Inputs
` path `
: A store path to convert to a derivation .
# Type
` ` `
toDerivation : : Path -> Derivation
` ` `
* /
toDerivation =
path :
let
path' = builtins . storePath path ;
res = {
type = " d e r i v a t i o n " ;
name = sanitizeDerivationName ( builtins . substring 33 ( -1 ) ( baseNameOf path' ) ) ;
outPath = path' ;
outputs = [ " o u t " ] ;
out = res ;
outputName = " o u t " ;
} ;
in
res ;
/* *
If ` cond ` is true , return the attribute set ` as ` ,
otherwise an empty attribute set .
# Inputs
` cond `
: Condition under which the ` as ` attribute set is returned .
` as `
: The attribute set to return if ` cond ` is ` true ` .
# Type
` ` `
optionalAttrs : : Bool -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.optionalAttrs` usage example
` ` ` nix
optionalAttrs ( true ) { my = " s e t " ; }
= > { my = " s e t " ; }
optionalAttrs ( false ) { my = " s e t " ; }
= > { }
` ` `
: : :
* /
optionalAttrs = cond : as : if cond then as else { } ;
/* *
Merge sets of attributes and use the function ` f ` to merge attributes
values .
# Inputs
` names `
: List of attribute names to zip .
` f `
: A function , accepts an attribute name , all the values , and returns a combined value .
` sets `
: List of values from the list of attribute sets .
# Type
` ` `
zipAttrsWithNames : : [ String ] -> ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.zipAttrsWithNames` usage example
` ` ` nix
zipAttrsWithNames [ " a " ] ( name : vs : vs ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
= > { a = [ " x " " y " ] ; }
` ` `
: : :
* /
zipAttrsWithNames =
names : f : sets :
listToAttrs (
map ( name : {
inherit name ;
value = f name ( catAttrs name sets ) ;
} ) names
) ;
/* *
Merge sets of attributes and use the function f to merge attribute values .
Like ` lib . attrsets . zipAttrsWithNames ` with all key names are passed for ` names ` .
Implementation note : Common names appear multiple times in the list of
names , hopefully this does not affect the system because the maximal
laziness avoid computing twice the same expression and ` listToAttrs ` does
not care about duplicated attribute names .
# Type
` ` `
zipAttrsWith : : ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.zipAttrsWith` usage example
` ` ` nix
zipAttrsWith ( name : values : values ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
` ` `
: : :
* /
zipAttrsWith =
builtins . zipAttrsWith or ( f : sets : zipAttrsWithNames ( concatMap attrNames sets ) f sets ) ;
/* *
Merge sets of attributes and combine each attribute value in to a list .
Like ` lib . attrsets . zipAttrsWith ` with ` ( name : values : values ) ` as the function .
# Type
` ` `
zipAttrs : : [ AttrSet ] -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.zipAttrs` usage example
` ` ` nix
zipAttrs [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
` ` `
: : :
* /
zipAttrs = zipAttrsWith ( name : values : values ) ;
/* *
Merge a list of attribute sets together using the ` // ` operator .
In case of duplicate attributes , values from later list elements take precedence over earlier ones .
The result is the same as ` foldl mergeAttrs { } ` , but the performance is better for large inputs .
For n list elements , each with an attribute set containing m unique attributes , the complexity of this operation is O ( nm log n ) .
# Inputs
` list `
: 1 \ . Function argument
# Type
` ` `
mergeAttrsList : : [ Attrs ] -> Attrs
` ` `
# Examples
: : : { . example }
## `lib.attrsets.mergeAttrsList` usage example
` ` ` nix
mergeAttrsList [ { a = 0 ; b = 1 ; } { c = 2 ; d = 3 ; } ]
= > { a = 0 ; b = 1 ; c = 2 ; d = 3 ; }
mergeAttrsList [ { a = 0 ; } { a = 1 ; } ]
= > { a = 1 ; }
` ` `
: : :
* /
mergeAttrsList =
list :
let
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
# Type: Int -> Int -> Attrs
binaryMerge =
start : end :
# assert start < end; # Invariant
if end - start >= 2 then
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
# The invariant is satisfied because each half will have at least 1 element
binaryMerge start ( start + ( end - start ) / 2 ) // binaryMerge ( start + ( end - start ) / 2 ) end
else
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
elemAt list start ;
in
if list == [ ] then
# Calling binaryMerge as below would not satisfy its invariant
{ }
else
binaryMerge 0 ( length list ) ;
/* *
Does the same as the update operator ' // ' except that attributes are
merged until the given predicate is verified . The predicate should
accept 3 arguments which are the path to reach the attribute , a part of
the first attribute set and a part of the second attribute set . When
the predicate is satisfied , the value of the first attribute set is
replaced by the value of the second attribute set .
# Inputs
` pred `
: Predicate , taking the path to the current attribute as a list of strings for attribute names , and the two values at that path from the original arguments .
` lhs `
: Left attribute set of the merge .
` rhs `
: Right attribute set of the merge .
# Type
` ` `
recursiveUpdateUntil : : ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.recursiveUpdateUntil` usage example
` ` ` nix
recursiveUpdateUntil ( path : l : r : path == [ " f o o " ] ) {
# first attribute set
foo . bar = 1 ;
foo . baz = 2 ;
bar = 3 ;
} {
#second attribute set
foo . bar = 1 ;
foo . quz = 2 ;
baz = 4 ;
}
= > {
foo . bar = 1 ; # 'foo.*' from the second set
foo . quz = 2 ; #
bar = 3 ; # 'bar' from the first set
baz = 4 ; # 'baz' from the second set
}
` ` `
: : :
* /
recursiveUpdateUntil =
pred : lhs : rhs :
let
f =
attrPath :
zipAttrsWith (
n : values :
let
here = attrPath ++ [ n ] ;
in
if length values == 1 || pred here ( elemAt values 1 ) ( head values ) then
head values
else
f here values
) ;
in
f [ ] [
rhs
lhs
] ;
/* *
A recursive variant of the update operator ‘ // ’ . The recursion
stops when one of the attribute values is not an attribute set ,
in which case the right hand side value takes precedence over the
left hand side value .
# Inputs
` lhs `
: Left attribute set of the merge .
` rhs `
: Right attribute set of the merge .
# Type
` ` `
recursiveUpdate : : AttrSet -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.recursiveUpdate` usage example
` ` ` nix
recursiveUpdate {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " / d e v / h d a " ;
} {
boot . loader . grub . device = " " ;
}
returns : {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " " ;
}
` ` `
: : :
* /
recursiveUpdate =
lhs : rhs :
recursiveUpdateUntil (
path : lhs : rhs :
! ( isAttrs lhs && isAttrs rhs )
) lhs rhs ;
/* *
Recurse into every attribute set of the first argument and check that :
- Each attribute path also exists in the second argument .
- If the attribute's value is not a nested attribute set , it must have the same value in the right argument .
# Inputs
` pattern `
: Attribute set structure to match
` attrs `
: Attribute set to check
# Type
` ` `
matchAttrs : : AttrSet -> AttrSet -> Bool
` ` `
# Examples
: : : { . example }
## `lib.attrsets.matchAttrs` usage example
` ` ` nix
matchAttrs { cpu = { } ; } { cpu = { bits = 64 ; } ; }
= > true
` ` `
: : :
* /
matchAttrs =
pattern : attrs :
assert isAttrs pattern ;
all (
# Compare equality between `pattern` & `attrs`.
attr :
# Missing attr, not equal.
attrs ? ${ attr }
&& (
let
lhs = pattern . ${ attr } ;
rhs = attrs . ${ attr } ;
in
# If attrset check recursively
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs else lhs == rhs
)
) ( attrNames pattern ) ;
/* *
Override only the attributes that are already present in the old set
useful for deep-overriding .
# Inputs
` old `
: Original attribute set
` new `
: Attribute set with attributes to override in ` old ` .
# Type
` ` `
overrideExisting : : AttrSet -> AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.overrideExisting` usage example
` ` ` nix
overrideExisting { } { a = 1 ; }
= > { }
overrideExisting { b = 2 ; } { a = 1 ; }
= > { b = 2 ; }
overrideExisting { a = 3 ; b = 2 ; } { a = 1 ; }
= > { a = 1 ; b = 2 ; }
` ` `
: : :
* /
overrideExisting = old : new : mapAttrs ( name : value : new . ${ name } or value ) old ;
/* *
Turns a list of strings into a human-readable description of those
strings represented as an attribute path . The result of this function is
not intended to be machine-readable .
Create a new attribute set with ` value ` set at the nested attribute location specified in ` attrPath ` .
# Inputs
` path `
: Attribute path to render to a string
# Type
` ` `
showAttrPath : : [ String ] -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.showAttrPath` usage example
` ` ` nix
showAttrPath [ " f o o " " 1 0 " " b a r " ]
= > " f o o . \" 1 0 \" . b a r "
showAttrPath [ ]
= > " < r o o t a t t r i b u t e p a t h > "
` ` `
: : :
* /
showAttrPath =
path :
if path == [ ] then " < r o o t a t t r i b u t e p a t h > " else concatMapStringsSep " . " escapeNixIdentifier path ;
/* *
Get a package output .
If no output is found , fallback to ` . out ` and then to the default .
# Inputs
` output `
: 1 \ . Function argument
` pkg `
: 2 \ . Function argument
# Type
` ` `
getOutput : : String -> Derivation -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getOutput` usage example
` ` ` nix
getOutput " d e v " pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
` ` `
: : :
* /
getOutput =
output : pkg :
if ! pkg ? outputSpecified || ! pkg . outputSpecified then pkg . ${ output } or pkg . out or pkg else pkg ;
/* *
Get a package's ` bin ` output .
If the output does not exist , fallback to ` . out ` and then to the default .
# Inputs
` pkg `
: The package whose ` bin ` output will be retrieved .
# Type
` ` `
getBin : : Derivation -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getBin` usage example
` ` ` nix
getBin pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r "
` ` `
: : :
* /
getBin = getOutput " b i n " ;
/* *
Get a package's ` lib ` output .
If the output does not exist , fallback to ` . out ` and then to the default .
# Inputs
` pkg `
: The package whose ` lib ` output will be retrieved .
# Type
` ` `
getLib : : Derivation -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getLib` usage example
` ` ` nix
getLib pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - l i b "
` ` `
: : :
* /
getLib = getOutput " l i b " ;
/* *
Get a package's ` dev ` output .
If the output does not exist , fallback to ` . out ` and then to the default .
# Inputs
` pkg `
: The package whose ` dev ` output will be retrieved .
# Type
` ` `
getDev : : Derivation -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getDev` usage example
` ` ` nix
getDev pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
` ` `
: : :
* /
getDev = getOutput " d e v " ;
/* *
Get a package's ` man ` output .
If the output does not exist , fallback to ` . out ` and then to the default .
# Inputs
` pkg `
: The package whose ` man ` output will be retrieved .
# Type
` ` `
getMan : : Derivation -> String
` ` `
# Examples
: : : { . example }
## `lib.attrsets.getMan` usage example
` ` ` nix
getMan pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - m a n "
` ` `
: : :
* /
getMan = getOutput " m a n " ;
/* *
Pick the outputs of packages to place in ` buildInputs `
# Inputs
` pkgs `
: List of packages .
# Type
` ` `
chooseDevOutputs : : [ Derivation ] -> [ String ]
` ` `
* /
chooseDevOutputs = builtins . map getDev ;
/* *
Make various Nix tools consider the contents of the resulting
attribute set when looking for what to build , find , etc .
This function only affects a single attribute set ; it does not
apply itself recursively for nested attribute sets .
# Inputs
` attrs `
: An attribute set to scan for derivations .
# Type
` ` `
recurseIntoAttrs : : AttrSet -> AttrSet
` ` `
# Examples
: : : { . example }
## `lib.attrsets.recurseIntoAttrs` usage example
` ` ` nix
{ pkgs ? import <nixpkgs> { } }:
{
myTools = pkgs . lib . recurseIntoAttrs {
inherit ( pkgs ) hello figlet ;
} ;
}
` ` `
: : :
* /
recurseIntoAttrs = attrs : attrs // { recurseForDerivations = true ; } ;
/* *
Undo the effect of recurseIntoAttrs .
# Inputs
` attrs `
: An attribute set to not scan for derivations .
# Type
` ` `
dontRecurseIntoAttrs : : AttrSet -> AttrSet
` ` `
* /
dontRecurseIntoAttrs = attrs : attrs // { recurseForDerivations = false ; } ;
/* *
` unionOfDisjoint x y ` is equal to ` x // y // z ` where the
attrnames in ` z ` are the intersection of the attrnames in ` x ` and
` y ` , and all values ` assert ` with an error message . This
operator is commutative , unlike ( // ) .
# Inputs
` x `
: 1 \ . Function argument
` y `
: 2 \ . Function argument
# Type
` ` `
unionOfDisjoint : : AttrSet -> AttrSet -> AttrSet
` ` `
* /
unionOfDisjoint =
x : y :
let
intersection = builtins . intersectAttrs x y ;
collisions = lib . concatStringsSep " " ( builtins . attrNames intersection ) ;
mask = builtins . mapAttrs (
name : value : builtins . throw " u n i o n O f D i s j o i n t : c o l l i s i o n o n ${ name } ; c o m p l e t e l i s t : ${ collisions } "
) intersection ;
in
( x // y ) // mask ;
# DEPRECATED
zipWithNames = warn " l i b . z i p W i t h N a m e s i s a d e p r e c a t e d a l i a s o f l i b . z i p A t t r s W i t h N a m e s . " zipAttrsWithNames ;
# DEPRECATED
zip = warn " l i b . z i p i s a d e p r e c a t e d a l i a s o f l i b . z i p A t t r s W i t h . " zipAttrsWith ;
# DEPRECATED
cartesianProductOfSets = warnIf ( isInOldestRelease
2405
) " l i b . c a r t e s i a n P r o d u c t O f S e t s i s a d e p r e c a t e d a l i a s o f l i b . c a r t e s i a n P r o d u c t . " cartesianProduct ;
}