fake.modules transposition for aspect-oriented Dendritic Nix. with cross-aspect dependencies. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ dendrix.oeiuwq.com/Dendritic.html
dendritic nix aspect oriented

fix: avoid merging results of functor. dont use types.functionTo merge (#38)

authored by oeiuwq.com and committed by

GitHub ccc25fc1 82a47265

+95 -1
+65
checkmate/modules/tests/aspect_functor_merge.nix
··· 1 + # Test that when multiple modules define the same aspect with a custom 2 + # __functor twice, the functor is not called once per definition (which would 3 + # duplicate the includes). Using lib.types.functionTo merges both functors. 4 + # 5 + # This was a bug where `functionTo` merge would 6 + # invoke all definitions and merge their results, causing duplication 7 + # when the functor uses `self` (the merged aspect) to produce includes. 8 + # 9 + # See https://github.com/vic/den/issues/216 10 + { lib, new-scope, ... }: 11 + { 12 + 13 + flake.tests."test functor merge does not duplicate includes" = 14 + let 15 + first = lib.evalModules { 16 + modules = [ 17 + (new-scope "kit") 18 + # Two separate modules defining the same aspect with same __functor. 19 + { 20 + kit.aspects.groups = { 21 + myclass.names = [ "alice" ]; 22 + __functor = self: { 23 + inherit (self) myclass; 24 + }; 25 + }; 26 + } 27 + { 28 + kit.aspects.groups = { 29 + myclass.names = [ "bob" ]; 30 + __functor = self: { 31 + inherit (self) myclass; 32 + }; 33 + }; 34 + } 35 + ( 36 + { config, ... }: 37 + { 38 + kit.aspects.main = { 39 + myclass.names = [ "main" ]; 40 + includes = [ config.kit.aspects.groups ]; 41 + }; 42 + } 43 + ) 44 + ]; 45 + }; 46 + 47 + second = lib.evalModules { 48 + modules = [ 49 + { options.names = lib.mkOption { type = lib.types.listOf lib.types.str; }; } 50 + first.config.kit.modules.myclass.main 51 + ]; 52 + }; 53 + 54 + expr = lib.sort (a: b: a < b) second.config.names; 55 + expected = [ 56 + "alice" 57 + "bob" 58 + "main" 59 + ]; 60 + in 61 + { 62 + inherit expr expected; 63 + }; 64 + 65 + }
+30 -1
nix/types.nix
··· 24 24 apply = fn; 25 25 }; 26 26 27 + # Like lib.types.functionTo, but it does not merges all definitions, and keeps 28 + # just the last one. 29 + functorType = lib.mkOptionType { 30 + name = "aspectFunctor"; 31 + description = "aspect functor function"; 32 + check = lib.isFunction; 33 + merge = 34 + loc: defs: 35 + let 36 + # Use only the last definition to avoid duplication from 37 + # functionTo merging all definitions with the same args. 38 + # All definitions receive the same merged `self`, so they 39 + # produce equivalent results - picking one is correct. 40 + lastDef = lib.last defs; 41 + innerType = providerType; 42 + in 43 + { 44 + __functionArgs = lib.functionArgs lastDef.value; 45 + __functor = 46 + _: callerArgs: 47 + (lib.modules.mergeDefinitions (loc ++ [ "<function body>" ]) innerType [ 48 + { 49 + inherit (lastDef) file; 50 + value = lastDef.value callerArgs; 51 + } 52 + ]).mergedValue; 53 + }; 54 + }; 55 + 27 56 # Check if function has submodule-style arguments 28 57 isSubmoduleFn = 29 58 m: ··· 101 130 internal = true; 102 131 visible = false; 103 132 description = "Functor to default provider"; 104 - type = lib.types.functionTo providerType; 133 + type = functorType; 105 134 default = aspect: { class, aspect-chain }: if true || (class aspect-chain) then aspect else aspect; 106 135 }; 107 136