Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(core): add fixedTo.{atLeast,exactly,upTo} (#340)

## Overview
Adds new features, early draft, wanted to get your opinion on the design
before committing to cleaning it up and potentially breaking it into
smaller focused PRs. Needs documentation update too.

#### `den.lib.take.upTo`
Uses the same predicate as `atLeast` to decide whether to invoke a
function, but calls the function with only the arguments it supports
unlike `atLeast` which will call the function with extra arguments
causing an error unless the function signature uses `...`. In my opinion
this is how `atLeast` should function, but I understand that this is a
breaking change if anyone is using `args@{ ctxKey, ... }` to capture the
full context. So I've introduced this as a seperate parametric type.

#### `den.lib.canTake.upTo`
An alias of `den.lib.canTake.atLeast` provided for API symmetry between
`den.lib.canTake` and `den.lib.take`

#### `den.lib.recursiveFunctor`
Much like `den.lib.functor` this takes an `apply` function and an aspect
and applies it to each include in the aspects `includes` list. However,
this also recurses into the include list and applies the function down
the include tree. Much like `fixedTo` does but without owned or static
aspects.

*Caveat:* This currently doesn't resolve static or parametric includes
and then recurse into their includes if they exist, should it? I'm not
sure why you would write aspects with this configuration, but it's
theoretically possible. I originally didn't implement this because I
thought it would be too complicated, but I think you can just call the
aspects with ctx and static ctx and then recurse into them to get at
them?

#### `den.lib.parametric.fixedTo.{exactly,atLeast,upTo}`
This family of `fixedTo` variants, follow `fixedTo`'s ability to recurse
into the import tree, but do not evaluate owned or static aspects. This
reflects the relationship between `parametric.__functor` and
`parametric.{exactly,atLeast,upTo}`.

---------

Co-authored-by: Victor Borja <vborja@apache.org>

authored by

Dylan R. Johnston
Victor Borja
and committed by
GitHub
6f4179c4 8101ec86

+488 -19
+3 -2
nix/lib/can-take.nix
··· 7 7 args = lib.functionArgs func; 8 8 required = builtins.filter (n: !args.${n}) (builtins.attrNames args); 9 9 intersect = builtins.intersectAttrs args params; 10 + satisfied = valid && builtins.all (n: params ? ${n}) required; 10 11 in 11 12 { 12 - satisfied = valid && builtins.all (n: params ? ${n}) required; 13 + satisfied = satisfied; 13 14 exactly = valid && required == builtins.attrNames params; 14 - upTo = valid && intersect != { }; 15 + upTo = satisfied && intersect != { }; 15 16 }; 16 17 in 17 18 {
+25 -15
nix/lib/parametric.nix
··· 12 12 }; 13 13 }; 14 14 15 - deepRecurse = 16 - functor: ctx: provided: 17 - provided 15 + mapIncludes = 16 + branch: leaf: aspect: 17 + aspect 18 18 // { 19 19 includes = map ( 20 - include: 21 - if include ? includes then 22 - if include ? __functor then include else parametric.deep functor ctx include 23 - else 24 - include 25 - ) (provided.includes or [ ]); 20 + include: if include ? includes && !include ? __functor then branch include else leaf include 21 + ) (aspect.includes or [ ]); 26 22 }; 27 23 28 24 parametric.atLeast = parametric.applyIncludes take.atLeast; ··· 32 28 parametric.expands = 33 29 attrs: parametric.withOwn (aspect: ctx: parametric.atLeast aspect (ctx // attrs)); 34 30 35 - parametric.deep = 36 - functor: attrs: aspect: 31 + deepRecurse = 32 + include: branch: leaf: aspect: 37 33 aspect 38 34 // { 39 35 __functor = ··· 41 37 { class, aspect-chain }: 42 38 { 43 39 includes = [ 44 - (owned self) 45 - (statics self { inherit class aspect-chain; }) 46 - (deepRecurse functor attrs (functor self attrs)) 40 + (include self { inherit class aspect-chain; }) 41 + (mapIncludes (deepRecurse include branch leaf) leaf (branch aspect)) 47 42 ]; 48 43 }; 49 44 }; 50 45 51 - parametric.fixedTo = parametric.deep parametric.atLeast; 46 + includeOwnedAndStatics = self: staticCtx: { 47 + includes = [ 48 + (owned self) 49 + (statics self staticCtx) 50 + ]; 51 + }; 52 + 53 + includeNothing = (_: _: { }); 54 + 55 + parametric.deep = functor: deepRecurse includeOwnedAndStatics functor lib.id; 56 + parametric.deepParametrics = functor: deepRecurse includeNothing lib.id functor; 57 + 58 + parametric.fixedTo.__functor = _: attrs: parametric.deep (lib.flip parametric.atLeast attrs); 59 + parametric.fixedTo.exactly = attrs: parametric.deepParametrics (lib.flip take.exactly attrs); 60 + parametric.fixedTo.atLeast = attrs: parametric.deepParametrics (lib.flip take.atLeast attrs); 61 + parametric.fixedTo.upTo = attrs: parametric.deepParametrics (lib.flip take.upTo attrs); 52 62 53 63 parametric.withOwn = 54 64 functor: aspect:
+1 -2
nix/lib/take.nix
··· 7 7 8 8 take.exactly = take den.lib.canTake.exactly asIs; 9 9 take.atLeast = take den.lib.canTake.atLeast asIs; 10 - take.upTo = take den.lib.canTake.atLeast upTo; 10 + take.upTo = take den.lib.canTake.upTo upTo; 11 11 12 12 take.__functor = 13 13 _: canTake: argAdapter: fn: args: ··· 15 15 ctx = argAdapter fn args; 16 16 in 17 17 if canTake ctx fn then fn ctx else { }; 18 - 19 18 in 20 19 take
+459
templates/ci/modules/features/parametric-fixedTo.nix
··· 1 + { denTest, lib, ... }: 2 + { 3 + flake.tests.parametric-fixedTo = 4 + let 5 + test-option = { 6 + nixos.options.test = lib.mkOption { type = lib.types.listOf lib.types.str; }; 7 + }; 8 + 9 + owned = aspect-name: { 10 + nixos.test = [ "${aspect-name}-owned" ]; 11 + }; 12 + 13 + parametric-include = 14 + aspect-name: 15 + { host, ... }: 16 + { 17 + includes = [ 18 + { 19 + nixos.test = [ "${aspect-name}-parametric-include-${host.name}" ]; 20 + } 21 + ]; 22 + }; 23 + 24 + nested-parametric = aspect-name: { 25 + includes = [ 26 + ( 27 + { host, ... }: 28 + { 29 + nixos.test = [ "${aspect-name}-nested-parametric-${host.name}" ]; 30 + } 31 + ) 32 + ]; 33 + }; 34 + 35 + static = 36 + aspect-name: 37 + { class, ... }: 38 + { 39 + nixos.test = [ "${aspect-name}-static-${class}" ]; 40 + }; 41 + 42 + parametric-exactly-host = 43 + aspect-name: 44 + { host }: 45 + { 46 + nixos.test = [ "${aspect-name}-parametric-exactly-host-${host.name}" ]; 47 + }; 48 + 49 + parametric-exactly-user = 50 + aspect-name: 51 + { user }: 52 + { 53 + nixos.test = [ "${aspect-name}-parametric-exactly-user-${user.userName}" ]; 54 + }; 55 + 56 + parametric-atLeast-host = 57 + aspect-name: 58 + { host, ... }: 59 + { 60 + nixos.test = [ "${aspect-name}-parametric-atLeast-host-${host.name}" ]; 61 + }; 62 + 63 + parametric-exactly-host-user = 64 + aspect-name: 65 + { host, user }: 66 + { 67 + nixos.test = [ "${aspect-name}-parametric-exactly-host-user-${host.name}-${user.userName}" ]; 68 + }; 69 + 70 + parametric-atLeast-user = 71 + aspect-name: 72 + { user, ... }: 73 + { 74 + nixos.test = [ "${aspect-name}-parametric-atLeast-user-${user.userName}" ]; 75 + }; 76 + 77 + named-aspect = aspect-name: includes: { 78 + includes = map (include: include aspect-name) includes; 79 + }; 80 + 81 + test-aspect = 82 + includes: 83 + let 84 + inner = _: named-aspect "inner" includes; 85 + in 86 + named-aspect "outer" (includes ++ lib.singleton inner); 87 + in 88 + { 89 + test-base-parametric-host-context = 90 + let 91 + aspects = [ 92 + owned 93 + static 94 + parametric-include 95 + parametric-exactly-host 96 + parametric-exactly-user 97 + parametric-exactly-host-user 98 + parametric-atLeast-host 99 + parametric-atLeast-user 100 + ]; 101 + in 102 + denTest ( 103 + { igloo, ... }: 104 + { 105 + den.hosts.x86_64-linux.igloo.users.tux = { }; 106 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 107 + 108 + den.ctx.host.includes = [ 109 + test-option 110 + (test-aspect aspects) 111 + ]; 112 + 113 + expr = lib.sort lib.lessThan igloo.test; 114 + expected = [ 115 + "inner-owned" 116 + "inner-parametric-atLeast-host-igloo" 117 + "inner-parametric-exactly-host-igloo" 118 + "inner-parametric-include-igloo" 119 + "inner-static-nixos" 120 + "outer-owned" 121 + "outer-parametric-atLeast-host-igloo" 122 + "outer-parametric-exactly-host-igloo" 123 + "outer-parametric-include-igloo" 124 + "outer-static-nixos" 125 + ]; 126 + } 127 + ); 128 + 129 + test-base-parametric-user-context = 130 + let 131 + aspects = [ 132 + owned 133 + static 134 + parametric-include 135 + # This causes an error with the base parametric type as they're called with extra types 136 + # parametric-exactly-host 137 + # parametric-exactly-user 138 + parametric-exactly-host-user 139 + parametric-atLeast-host 140 + parametric-atLeast-user 141 + ]; 142 + in 143 + denTest ( 144 + { igloo, ... }: 145 + { 146 + den.hosts.x86_64-linux.igloo.users.tux = { }; 147 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 148 + 149 + den.ctx.host.includes = [ 150 + test-option 151 + ]; 152 + 153 + den.ctx.user.includes = [ 154 + (test-aspect aspects) 155 + ]; 156 + 157 + expr = lib.sort lib.lessThan igloo.test; 158 + 159 + # We expect double inclusion of atLeast-host because it is included by both user contexts 160 + expected = [ 161 + "inner-owned" 162 + "inner-parametric-atLeast-host-igloo" 163 + "inner-parametric-atLeast-host-igloo" 164 + "inner-parametric-atLeast-user-gnu" 165 + "inner-parametric-atLeast-user-tux" 166 + "inner-parametric-exactly-host-user-igloo-gnu" 167 + "inner-parametric-exactly-host-user-igloo-tux" 168 + "inner-parametric-include-igloo" 169 + "inner-parametric-include-igloo" 170 + "inner-static-nixos" 171 + "outer-owned" 172 + "outer-parametric-atLeast-host-igloo" 173 + "outer-parametric-atLeast-host-igloo" 174 + "outer-parametric-atLeast-user-gnu" 175 + "outer-parametric-atLeast-user-tux" 176 + "outer-parametric-exactly-host-user-igloo-gnu" 177 + "outer-parametric-exactly-host-user-igloo-tux" 178 + "outer-parametric-include-igloo" 179 + "outer-parametric-include-igloo" 180 + "outer-static-nixos" 181 + ]; 182 + } 183 + ); 184 + 185 + test-base-parametric-fixedTo-exactly-host-context = 186 + let 187 + aspects = [ 188 + owned 189 + static 190 + parametric-include 191 + nested-parametric 192 + parametric-exactly-host 193 + parametric-exactly-user 194 + parametric-exactly-host-user 195 + parametric-atLeast-host 196 + parametric-atLeast-user 197 + ]; 198 + in 199 + denTest ( 200 + { den, igloo, ... }: 201 + { 202 + den.hosts.x86_64-linux.igloo.users.tux = { }; 203 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 204 + 205 + den.ctx.host.includes = [ 206 + test-option 207 + ({ host }: den.lib.parametric.fixedTo.exactly { inherit host; } (test-aspect aspects)) 208 + ]; 209 + 210 + expr = lib.sort lib.lessThan igloo.test; 211 + expected = [ 212 + "inner-nested-parametric-igloo" 213 + "inner-parametric-atLeast-host-igloo" 214 + "inner-parametric-exactly-host-igloo" 215 + "inner-parametric-include-igloo" 216 + "outer-nested-parametric-igloo" 217 + "outer-parametric-atLeast-host-igloo" 218 + "outer-parametric-exactly-host-igloo" 219 + "outer-parametric-include-igloo" 220 + ]; 221 + } 222 + ); 223 + 224 + test-base-parametric-fixedTo-exactly-user-context = 225 + let 226 + aspects = [ 227 + owned 228 + static 229 + parametric-include 230 + nested-parametric 231 + parametric-exactly-host 232 + parametric-exactly-user 233 + parametric-exactly-host-user 234 + parametric-atLeast-host 235 + parametric-atLeast-user 236 + ]; 237 + in 238 + denTest ( 239 + { den, igloo, ... }: 240 + { 241 + den.hosts.x86_64-linux.igloo.users.tux = { }; 242 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 243 + 244 + den.ctx.host.includes = [ 245 + test-option 246 + ]; 247 + 248 + den.ctx.user.includes = [ 249 + ({ host, user }: den.lib.parametric.fixedTo.exactly { inherit host user; } (test-aspect aspects)) 250 + ]; 251 + 252 + expr = lib.sort lib.lessThan igloo.test; 253 + expected = [ 254 + "inner-parametric-exactly-host-user-igloo-gnu" 255 + "inner-parametric-exactly-host-user-igloo-tux" 256 + "outer-parametric-exactly-host-user-igloo-gnu" 257 + "outer-parametric-exactly-host-user-igloo-tux" 258 + ]; 259 + } 260 + ); 261 + 262 + test-base-parametric-fixedTo-atLeast-host-context = 263 + let 264 + aspects = [ 265 + owned 266 + static 267 + parametric-include 268 + nested-parametric 269 + parametric-exactly-host 270 + parametric-exactly-user 271 + parametric-exactly-host-user 272 + parametric-atLeast-host 273 + parametric-atLeast-user 274 + ]; 275 + in 276 + denTest ( 277 + { den, igloo, ... }: 278 + { 279 + den.hosts.x86_64-linux.igloo.users.tux = { }; 280 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 281 + 282 + den.ctx.host.includes = [ 283 + test-option 284 + ({ host }: den.lib.parametric.fixedTo.atLeast { inherit host; } (test-aspect aspects)) 285 + ]; 286 + 287 + den.ctx.user.includes = [ ]; 288 + 289 + expr = lib.sort lib.lessThan igloo.test; 290 + expected = [ 291 + "inner-nested-parametric-igloo" 292 + "inner-parametric-atLeast-host-igloo" 293 + "inner-parametric-exactly-host-igloo" 294 + "inner-parametric-include-igloo" 295 + "outer-nested-parametric-igloo" 296 + "outer-parametric-atLeast-host-igloo" 297 + "outer-parametric-exactly-host-igloo" 298 + "outer-parametric-include-igloo" 299 + ]; 300 + } 301 + ); 302 + 303 + test-base-parametric-fixedTo-atLeast-user-context = 304 + let 305 + aspects = [ 306 + owned 307 + static 308 + parametric-include 309 + nested-parametric 310 + # parametric-exactly-host 311 + # parametric-exactly-user 312 + parametric-exactly-host-user 313 + parametric-atLeast-host 314 + parametric-atLeast-user 315 + ]; 316 + in 317 + denTest ( 318 + { den, igloo, ... }: 319 + { 320 + den.hosts.x86_64-linux.igloo.users.tux = { }; 321 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 322 + 323 + den.ctx.host.includes = [ 324 + test-option 325 + ]; 326 + 327 + den.ctx.user.includes = [ 328 + ({ host, user }: den.lib.parametric.fixedTo.atLeast { inherit host user; } (test-aspect aspects)) 329 + ]; 330 + 331 + expr = lib.sort lib.lessThan igloo.test; 332 + expected = [ 333 + "inner-nested-parametric-igloo" 334 + "inner-nested-parametric-igloo" 335 + "inner-parametric-atLeast-host-igloo" 336 + "inner-parametric-atLeast-host-igloo" 337 + "inner-parametric-atLeast-user-gnu" 338 + "inner-parametric-atLeast-user-tux" 339 + "inner-parametric-exactly-host-user-igloo-gnu" 340 + "inner-parametric-exactly-host-user-igloo-tux" 341 + "inner-parametric-include-igloo" 342 + "inner-parametric-include-igloo" 343 + "outer-nested-parametric-igloo" 344 + "outer-nested-parametric-igloo" 345 + "outer-parametric-atLeast-host-igloo" 346 + "outer-parametric-atLeast-host-igloo" 347 + "outer-parametric-atLeast-user-gnu" 348 + "outer-parametric-atLeast-user-tux" 349 + "outer-parametric-exactly-host-user-igloo-gnu" 350 + "outer-parametric-exactly-host-user-igloo-tux" 351 + "outer-parametric-include-igloo" 352 + "outer-parametric-include-igloo" 353 + ]; 354 + } 355 + ); 356 + 357 + test-base-parametric-fixedTo-upTo-host-context = 358 + let 359 + aspects = [ 360 + owned 361 + static 362 + parametric-include 363 + nested-parametric 364 + parametric-exactly-host 365 + parametric-exactly-user 366 + parametric-exactly-host-user 367 + parametric-atLeast-host 368 + parametric-atLeast-user 369 + ]; 370 + in 371 + denTest ( 372 + { den, igloo, ... }: 373 + { 374 + den.hosts.x86_64-linux.igloo.users.tux = { }; 375 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 376 + 377 + den.ctx.host.includes = [ 378 + test-option 379 + ({ host }: den.lib.parametric.fixedTo.upTo { inherit host; } (test-aspect aspects)) 380 + ]; 381 + 382 + expr = lib.sort lib.lessThan igloo.test; 383 + expected = [ 384 + "inner-nested-parametric-igloo" 385 + "inner-parametric-atLeast-host-igloo" 386 + "inner-parametric-exactly-host-igloo" 387 + "inner-parametric-include-igloo" 388 + "outer-nested-parametric-igloo" 389 + "outer-parametric-atLeast-host-igloo" 390 + "outer-parametric-exactly-host-igloo" 391 + "outer-parametric-include-igloo" 392 + ]; 393 + } 394 + ); 395 + 396 + test-base-parametric-fixedTo-upTo-user-context = 397 + let 398 + aspects = [ 399 + owned 400 + static 401 + parametric-include 402 + nested-parametric 403 + parametric-exactly-host 404 + parametric-exactly-user 405 + parametric-exactly-host-user 406 + parametric-atLeast-host 407 + parametric-atLeast-user 408 + ]; 409 + in 410 + denTest ( 411 + { den, igloo, ... }: 412 + { 413 + den.hosts.x86_64-linux.igloo.users.tux = { }; 414 + den.hosts.x86_64-linux.igloo.users.gnu = { }; 415 + 416 + den.ctx.host.includes = [ 417 + test-option 418 + ]; 419 + 420 + den.ctx.user.includes = [ 421 + ({ host, user }: den.lib.parametric.fixedTo.upTo { inherit host user; } (test-aspect aspects)) 422 + ]; 423 + 424 + expr = lib.sort lib.lessThan igloo.test; 425 + expected = [ 426 + "inner-nested-parametric-igloo" 427 + "inner-nested-parametric-igloo" 428 + "inner-parametric-atLeast-host-igloo" 429 + "inner-parametric-atLeast-host-igloo" 430 + "inner-parametric-atLeast-user-gnu" 431 + "inner-parametric-atLeast-user-tux" 432 + "inner-parametric-exactly-host-igloo" 433 + "inner-parametric-exactly-host-igloo" 434 + "inner-parametric-exactly-host-user-igloo-gnu" 435 + "inner-parametric-exactly-host-user-igloo-tux" 436 + "inner-parametric-exactly-user-gnu" 437 + "inner-parametric-exactly-user-tux" 438 + "inner-parametric-include-igloo" 439 + "inner-parametric-include-igloo" 440 + "outer-nested-parametric-igloo" 441 + "outer-nested-parametric-igloo" 442 + "outer-parametric-atLeast-host-igloo" 443 + "outer-parametric-atLeast-host-igloo" 444 + "outer-parametric-atLeast-user-gnu" 445 + "outer-parametric-atLeast-user-tux" 446 + "outer-parametric-exactly-host-igloo" 447 + "outer-parametric-exactly-host-igloo" 448 + "outer-parametric-exactly-host-user-igloo-gnu" 449 + "outer-parametric-exactly-host-user-igloo-tux" 450 + "outer-parametric-exactly-user-gnu" 451 + "outer-parametric-exactly-user-tux" 452 + "outer-parametric-include-igloo" 453 + "outer-parametric-include-igloo" 454 + ]; 455 + } 456 + ); 457 + 458 + }; 459 + }