-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Single package fixpoint step 2: introduce mkPackage
#296769
Draft
roberth
wants to merge
13
commits into
NixOS:master
Choose a base branch
from
hercules-ci:single-fixpoint
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+590
−101
Draft
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
2b6800a
lib: Add encapsulate
roberth 8526b44
Add mkPackageWithDeps
roberth 0167832
mkPackageWithDeps: Allow non-pkgs deps
roberth 1560545
Make layers overridable
roberth e674f5f
callPackage: Special treatment for mkPackage
roberth 5314019
layers.buildNpmPackage: init
roberth 5c529c6
mkPackage: Expose fixpoint root as .internals
roberth 3ca396c
callPackage: Add comment about callPackage/mkPackage usage
roberth f5c9bdd
netlify-cli: Use mkPackage
roberth 91b06a3
fixup! netlify-cli: Use mkPackage
roberth 7f61ae8
lib.customisation.hydraJob: system is optional
roberth 5e36866
hello.src: Restore
roberth b5666e9
test: Accept streamNixShellImage reliance on internals for now
roberth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -451,6 +451,96 @@ rec { | |
} | ||
); | ||
|
||
/* | ||
Creates an overridable attrset with encapsulation. | ||
|
||
This is like `makeExtensible`, but only the `public` attribute of the fixed | ||
point is returned. | ||
|
||
Synopsis: | ||
|
||
r = encapsulate (final@{extend, ...}: { | ||
|
||
# ... private attributes for `final` ... | ||
|
||
public = { | ||
# ... returned attributes for r, in terms of `final` ... | ||
inherit extend; # optional, don't invoke too often; see below | ||
}; | ||
}) | ||
|
||
s = r.extend (final: previous: { | ||
|
||
# ... updates to private attributes ... | ||
|
||
# optionally | ||
public = previous.public // { | ||
# ... updates to public attributes ... | ||
}; | ||
}) | ||
|
||
= Performance | ||
|
||
The `extend` function evaluates the whole fixed point all over, reusing | ||
no "intermediate results" from the existing object. | ||
This is necessary, because `final` has changed. | ||
So the cost is quadratic; O(n^2) where n = number of chained invocations. | ||
This has consequences for interface design. | ||
Although enticing, `extend` is not suitable for directly implementing "fluent interfaces", where the caller makes many calls to `extend` via domain-specific "setters" or `with*` functions. | ||
Fluent interfaces can not be implemented efficiently in Nix and have very little to offer over attribute sets in terms of usability.* | ||
|
||
Example: | ||
|
||
# cd nixpkgs; nix repl lib | ||
|
||
nix-repl> multiplier = encapsulate (self: { | ||
a = 1; | ||
b = 1; | ||
public = { | ||
r = self.a * self.b; | ||
|
||
# Publishing extend makes the attrset open for any kind of change. | ||
inherit (self) extend; | ||
|
||
# Instead, or additionally, you can add domain-specific functions. | ||
# Offer a single method with multiple arguments, and not a | ||
# "fluent interface" of a method per argument, because all extension | ||
# functions are called for every `extend`. See the Performance section. | ||
withParams = args@{ a ? null, b ? null }: # NB: defaults are not used | ||
self.extend (self: super: args); | ||
|
||
}; | ||
}) | ||
|
||
nix-repl> multiplier | ||
{ extend = «lambda»; r = 1; withParams =«lambda»; } | ||
|
||
nix-repl> multiplier.withParams { a = 42; b = 10; } | ||
{ extend = «lambda»; r = 420; withParams =«lambda»; } | ||
|
||
nix-repl> multiplier3 = multiplier.extend (self: super: { | ||
c = 1; | ||
public = super.public // { | ||
r = super.public.r * self.c; | ||
}; | ||
}) | ||
|
||
nix-repl> multiplier3.extend (self: super: { a = 2; b = 3; c = 10; }) | ||
{ extend = «lambda»; r = 60; withParams =«lambda»; } | ||
|
||
(*) Final note on Fluent APIs: While the asymptotic complexity can be fixed | ||
by avoiding overlay extension or perhaps using it only at the end of the | ||
chain only, one problem remains. Every method invocation has to produce | ||
a new, immutable state value, which means copying the whole state up to | ||
that point. | ||
*/ | ||
encapsulate = | ||
layerZero: | ||
let | ||
fixed = layerZero ({ extend = f: encapsulate (extends f layerZero); } // fixed); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This forms the new fixpoint. |
||
in | ||
fixed.public; | ||
|
||
/** | ||
Convert to an extending function (overlay). | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
`mkPackage` layer for building NPM packages. | ||
*/ | ||
{ | ||
lib, | ||
stdenv, | ||
fetchNpmDeps, | ||
buildPackages, | ||
nodejs, | ||
cctools, | ||
}: | ||
|
||
let | ||
# The fetcher needs the unpack related attributes | ||
fetchInherited = { | ||
src = null; | ||
srcs = null; | ||
sourceRoot = null; | ||
prePatch = null; | ||
patches = null; | ||
postPatch = null; | ||
patchFlags = null; | ||
}; | ||
in | ||
this: old: | ||
let | ||
inherit (this) deps name version; | ||
in | ||
{ | ||
deps = { | ||
inherit (deps.nodejs) fetchNpmDeps; | ||
npmHooks = buildPackages.npmHooks.override { | ||
inherit (deps) nodejs; | ||
}; | ||
inherit (deps.npmHooks) npmConfigHook npmBuildHook npmInstallHook; | ||
} // old.deps; | ||
/** | ||
Arguments for fetchNpmDeps | ||
*/ | ||
npmFetch = | ||
{ | ||
name = "${name}-${version}-npm-deps"; | ||
hash = throw "Please specify npmFetch.hash in the package definition of ${name}"; | ||
forceEmptyCache = false; | ||
forceGitDeps = false; | ||
patchFlags = ""; | ||
postPatch = ""; | ||
prePatch = ""; | ||
} | ||
// builtins.intersectAttrs fetchInherited this.setup | ||
// old.npmFetch; | ||
setup = old.setup or { } // { | ||
inherit (deps) nodejs; | ||
npmDeps = fetchNpmDeps this.npmFetch; | ||
npmPruneFlags = old.setup.npmPruneFlags or this.setup.npmInstallFlags or [ ]; | ||
npmBuildScript = "build"; | ||
nativeBuildInputs = | ||
old.setup.nativeBuildInputs | ||
++ [ | ||
deps.nodejs | ||
deps.npmConfigHook | ||
deps.npmBuildHook | ||
deps.npmInstallHook | ||
deps.nodejs.python | ||
] | ||
++ lib.optionals stdenv.hostPlatform.isDarwin [ cctools ]; | ||
buildInputs = old.setup.buildInputs or [ ] ++ [ deps.nodejs ]; | ||
strictDeps = true; | ||
# Stripping takes way too long with the amount of files required by a typical Node.js project. | ||
dontStrip = old.dontStrip or true; | ||
}; | ||
meta = (old.meta or { }) // { | ||
platforms = old.meta.platforms or deps.nodejs.meta.platforms; | ||
}; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This avoids adding the second fixpoint (
callPackage
/makeOverridable
/.override
).I should say that this is an overly broad interpretation of fixpoint, but a recursion is definitely there, so I'll just use the term. (even if
result
isn't passed back tomakeOverridable
'sf
, the overriding dynamics are equally problematic)