Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 184 additions & 1 deletion src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2999,6 +2999,12 @@ static RegisterPrimOp primop_attrNames({
Return the names of the attributes in the set *set* in an
alphabetically sorted list. For instance, `builtins.attrNames { y
= 1; x = "foo"; }` evaluates to `[ "x" "y" ]`.

# Time Complexity

- O(n log n), where:

n = number of attributes in the set
)",
.fun = prim_attrNames,
});
Expand Down Expand Up @@ -3031,6 +3037,12 @@ static RegisterPrimOp primop_attrValues({
.doc = R"(
Return the values of the attributes in the set *set* in the order
corresponding to the sorted attribute names.

# Time Complexity

- O(n log n), where:

n = number of attributes in the set
)",
.fun = prim_attrValues,
});
Expand All @@ -3056,6 +3068,10 @@ static RegisterPrimOp primop_getAttr({
aborts if the attribute doesn’t exist. This is a dynamic version of
the `.` operator, since *s* is an expression rather than an
identifier.

# Time Complexity

O(log n) where n = number of attributes in the set
)",
.fun = prim_getAttr,
});
Expand Down Expand Up @@ -3144,6 +3160,10 @@ static RegisterPrimOp primop_hasAttr({
`hasAttr` returns `true` if *set* has an attribute named *s*, and
`false` otherwise. This is a dynamic version of the `?` operator,
since *s* is an expression rather than an identifier.

# Time Complexity

O(log n) where n = number of attributes in the set
)",
.fun = prim_hasAttr,
});
Expand Down Expand Up @@ -3203,6 +3223,13 @@ static RegisterPrimOp primop_removeAttrs({
```

evaluates to `{ y = 2; }`.

# Time Complexity

O(n + k log k) where:

n = number of attributes in input set
k = number of attribute names to remove
)",
.fun = prim_removeAttrs,
});
Expand Down Expand Up @@ -3290,6 +3317,10 @@ static RegisterPrimOp primop_listToAttrs({
```nix
{ foo = 123; bar = 456; }
```

# Time Complexity

O(n log n) where n = number of list elements
)",
.fun = prim_listToAttrs,
});
Expand Down Expand Up @@ -3366,7 +3397,12 @@ static RegisterPrimOp primop_intersectAttrs({
Return a set consisting of the attributes in the set *e2* which have the
same name as some attribute in *e1*.

Performs in O(*n* log *m*) where *n* is the size of the smaller set and *m* the larger set's size.
# Time Complexity

O(n * log m) where:

n = number of attributes in the smaller set
m = number of attributes in the larger set
)",
.fun = prim_intersectAttrs,
});
Expand Down Expand Up @@ -3406,6 +3442,13 @@ static RegisterPrimOp primop_catAttrs({
```

evaluates to `[1 2]`.

# Time Complexity

O(n * log m) where:

n = number of sets in input list
m = number of attributes per set
)",
.fun = prim_catAttrs,
});
Expand Down Expand Up @@ -3449,6 +3492,10 @@ static RegisterPrimOp primop_functionArgs({
"Formal argument" here refers to the attributes pattern-matched by
the function. Plain lambdas are not included, e.g. `functionArgs (x:
...) = { }`.

# Time Complexity

O(n) where n = number of formal arguments
)",
.fun = prim_functionArgs,
});
Expand Down Expand Up @@ -3481,6 +3528,14 @@ static RegisterPrimOp primop_mapAttrs({
```

evaluates to `{ a = 10; b = 20; }`.

# Time Complexity

O(n) where:

n = number of attributes

Calls to `f` are performed afterwards, when needed
)",
.fun = prim_mapAttrs,
});
Expand Down Expand Up @@ -3568,6 +3623,15 @@ static RegisterPrimOp primop_zipAttrsWith({
b = { name = "b"; values = [ "z" ]; };
}
```

# Time Complexity

O(n * k * log k) worst case, where:

n = number of attribute sets in input list
k = number of unique keys across all sets

More precisely: O(n * m * log k) where m ≤ k is average number of attributes per set
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't checked this one.

)",
.fun = prim_zipAttrsWith,
});
Expand Down Expand Up @@ -3633,6 +3697,10 @@ static RegisterPrimOp primop_head({
Return the first element of a list; abort evaluation if the argument
isn’t a list or is an empty list. You can test whether a list is
empty by comparing it with `[]`.

# Time Complexity

O(1)
)",
.fun = prim_head,
});
Expand Down Expand Up @@ -3664,6 +3732,10 @@ static RegisterPrimOp primop_tail({
> This function should generally be avoided since it's inefficient:
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
> list by repeatedly calling `tail` takes O(n^2) time.

# Time Complexity

O(n) where n = list length (copies n-1 elements)
)",
.fun = prim_tail,
});
Expand Down Expand Up @@ -3698,6 +3770,14 @@ static RegisterPrimOp primop_map({
```

evaluates to `[ "foobar" "foobla" "fooabc" ]`.

# Time Complexity

O(n) where:

n = list length

Calls to `f` are performed afterwards when needed.
)",
.fun = prim_map,
});
Expand Down Expand Up @@ -3747,6 +3827,13 @@ static RegisterPrimOp primop_filter({
.doc = R"(
Return a list consisting of the elements of *list* for which the
function *f* returns `true`.

# Time Complexity

O(n * T_f) where:

n = list length
T_f = predicate evaluation time
)",
.fun = prim_filter,
});
Expand All @@ -3770,6 +3857,15 @@ static RegisterPrimOp primop_elem({
.doc = R"(
Return `true` if a value equal to *x* occurs in the list *xs*, and
`false` otherwise.

# Time Complexity

O(n * T) (worst case) where:

n = list length
T = time to compare two elements

returns early if the elements is found
)",
.fun = prim_elem,
});
Expand All @@ -3792,6 +3888,12 @@ static RegisterPrimOp primop_concatLists({
.args = {"lists"},
.doc = R"(
Concatenate a list of lists into a single list.

# Time Complexity

O(N) where:

N = total number of elements across all lists
)",
.fun = prim_concatLists,
});
Expand All @@ -3808,6 +3910,10 @@ static RegisterPrimOp primop_length({
.args = {"e"},
.doc = R"(
Return the length of the list *e*.

# Time Complexity

O(1)
)",
.fun = prim_length,
});
Expand Down Expand Up @@ -3851,6 +3957,17 @@ static RegisterPrimOp primop_foldlStrict({
argument is the current element being processed. The return value
of each application of `op` is evaluated immediately, even for
intermediate values.

# Time Complexity

O(n * T_op) where:

n = list length
T_op = `op` call evaluation time

Note: Because foldl' evaluates results only to [WHNF](@docroot@/language/evaluation.md#values),
deeper parts of values returned by op may be evaluated later.
Their cost (and errors) therefore may not clearly belong to T_op or to foldl' itself.
)",
.fun = prim_foldlStrict,
});
Expand Down Expand Up @@ -3889,6 +4006,15 @@ static RegisterPrimOp primop_any({
.doc = R"(
Return `true` if the function *pred* returns `true` for at least one
element of *list*, and `false` otherwise.

# Time Complexity

O(n * T_pred) where:

- n = `list` length
- T_pred = `pred` call evaluation time

returns early when `pred` returns `true`
)",
.fun = prim_any,
});
Expand All @@ -3904,6 +4030,13 @@ static RegisterPrimOp primop_all({
.doc = R"(
Return `true` if the function *pred* returns `true` for all elements
of *list*, and `false` otherwise.

# Time Complexity

O(n * T_f) where:

- n = list length
- T_f = predicate evaluation time
)",
.fun = prim_all,
});
Expand Down Expand Up @@ -3942,6 +4075,14 @@ static RegisterPrimOp primop_genList({
```

returns the list `[ 0 1 4 9 16 ]`.

# Time Complexity

O(n) where:

n = requested length.

Calls to `generator` are performed afterwards, when needed.
)",
.fun = prim_genList,
});
Expand Down Expand Up @@ -4036,6 +4177,13 @@ static RegisterPrimOp primop_sort({

If the *comparator* violates any of these properties, then `builtins.sort`
reorders elements in an unspecified manner.

# Time Complexity

O(n log n * T_cmp), where:

n = `list` length
T_cmp = `comparator` call evaluation time
)",
.fun = prim_sort,
});
Expand Down Expand Up @@ -4097,6 +4245,13 @@ static RegisterPrimOp primop_partition({
```nix
{ right = [ 23 42 ]; wrong = [ 1 9 3 ]; }
```

# Time Complexity

O(n * T_pred) where:

n = list length
T_pred = `pred` call evaluation time
)",
.fun = prim_partition,
});
Expand Down Expand Up @@ -4150,6 +4305,14 @@ static RegisterPrimOp primop_groupBy({
```nix
{ b = [ "bar" "baz" ]; f = [ "foo" ]; }
```

# Time Complexity

O(N * T_f + N * log k) where:

N = number of `list` elements
T_f = `f` call evaluation time
k = number of unique groups
)",
.fun = prim_groupBy,
});
Expand Down Expand Up @@ -4192,6 +4355,14 @@ static RegisterPrimOp primop_concatMap({
.doc = R"(
This function is equivalent to `builtins.concatLists (map f list)`
but is more efficient.

# Time Complexity

O(k * T_f + N) where:

k = length of input list
T_f = time to call `f` on an element
N = total number of elements returned by `f` calls
)",
.fun = prim_concatMap,
});
Expand Down Expand Up @@ -4888,6 +5059,10 @@ static RegisterPrimOp primop_concatStringsSep({
Concatenate a list of strings with a separator between each
element, e.g. `concatStringsSep "/" ["usr" "local" "bin"] ==
"usr/local/bin"`.

# Time Complexity

O(n) where n = total length of output string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically there's also the evaluation of each element, as this function is currently strict in those.
(I'm not sure that it will remain so! I've heard talk about lazy ropes)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O(n+m) where:
n = length of the list
m = length of the output string

)",
.fun = prim_concatStringsSep,
});
Expand Down Expand Up @@ -4972,6 +5147,14 @@ static RegisterPrimOp primop_replaceStrings({
```

evaluates to `"fabir"`.

# Time Complexity

O(n * k * c) where:

n = length of input string
k = number of replacement patterns
c = average length of patterns in 'from' list
)",
.fun = prim_replaceStrings,
});
Expand Down
Loading