From 54530f99e6381a6479507c657ed7073ccc0ea8fa Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Sun, 7 Jan 2024 16:42:51 +0100 Subject: [PATCH] add proper handling for functors --- flake.lock | 6 +- pasta/src/tools.nix | 12 +++- pesto/src/alias.rs | 7 ++ pesto/src/main.rs | 3 + pesto/src/pasta.rs | 2 + website/src/app/f/[...path]/page.tsx | 22 ++++++- .../src/app/md/tutorials/functors/page.mdx | 60 +++++++++++++++++ website/src/components/PositionLink.tsx | 64 +++++++++++++------ website/src/models/data/index.ts | 1 + 9 files changed, 149 insertions(+), 28 deletions(-) create mode 100644 website/src/app/md/tutorials/functors/page.mdx diff --git a/flake.lock b/flake.lock index c5eb170..1132cdb 100644 --- a/flake.lock +++ b/flake.lock @@ -187,11 +187,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1704367076, - "narHash": "sha256-wy+M/+mryU2w4HXO0cWiKeoL2IICR715q+yWacnSg4o=", + "lastModified": 1704635985, + "narHash": "sha256-1RePtidMIkpuBokpO0iPtVdaXO077a1GsInv25wRVhQ=", "owner": "hsjobeki", "repo": "nix", - "rev": "d86596aabc7e480858fd5d024deb8c4092200d47", + "rev": "0df2e1a93b905185b3e2118651109d9a57ec1593", "type": "github" }, "original": { diff --git a/pasta/src/tools.nix b/pasta/src/tools.nix index b5e5774..2a566e2 100644 --- a/pasta/src/tools.nix +++ b/pasta/src/tools.nix @@ -24,10 +24,16 @@ let let lambda = if lib.isFunction parent.${name} then - if parent.${name} ? __functor then - builtins.lambdaMeta (unwrapFunctor parent.${name}) + let + pos = builtins.lambdaMeta parent.${name}; + in + if parent.${name} ? __functor && pos ? position && pos.position == null then + let + res = builtins.lambdaMeta (unwrapFunctor parent.${name}); + in + res // { countApplied = (res.countApplied or 0) + 1; isFunctor = true; } else - builtins.lambdaMeta parent.${name} + pos else null; attr = { position = builtins.unsafeGetAttrPos name parent; }; diff --git a/pesto/src/alias.rs b/pesto/src/alias.rs index 411eca0..06f2549 100644 --- a/pesto/src/alias.rs +++ b/pesto/src/alias.rs @@ -122,6 +122,9 @@ pub fn categorize(data: &Vec) -> FnCategories { for item in data.iter() { if let Some(lambda) = &item.docs.lambda { + if lambda.isFunctor == Some(true) { + continue; + } match lambda.countApplied { // Some(0) | None => { Some(0) => { @@ -161,6 +164,10 @@ pub fn init_alias_map(data: &Vec, categories: FnCategories) -> AliasMap { let mut alias_map: AliasMap = HashMap::new(); for item in data.iter() { if let Some(lambda) = &item.docs.lambda { + // Skip functors + if lambda.isFunctor == Some(true) { + continue; + } match lambda.countApplied { Some(0) => { if lambda.isPrimop { diff --git a/pesto/src/main.rs b/pesto/src/main.rs index f55049c..171126f 100644 --- a/pesto/src/main.rs +++ b/pesto/src/main.rs @@ -171,6 +171,8 @@ struct DocumentFrontmatter<'a> { /// If an item is primop then it should have the PrimopMeta field. is_primop: Option, primop_meta: Option>, + /// is functor + is_functor: Option, /// Where the attribute is defined at. attr_position: Option<&'a FilePosition>, /// Where the original lambda is defined at. @@ -207,6 +209,7 @@ impl<'a> FromDocs<'a> for Document<'a> { .map(|i| i.position.as_ref()) .flatten(), is_primop: item.docs.lambda.as_ref().map(|i| i.isPrimop), + is_functor: item.docs.lambda.as_ref().map(|i| i.isFunctor).flatten(), count_applied: item.docs.lambda.as_ref().map(|i| i.countApplied).flatten(), primop_meta: match &item.docs.lambda { None => None, diff --git a/pesto/src/pasta.rs b/pesto/src/pasta.rs index 1bf8ad1..e4b5abd 100644 --- a/pesto/src/pasta.rs +++ b/pesto/src/pasta.rs @@ -18,6 +18,8 @@ use crate::position::FilePosition; pub struct LambdaMeta { #[allow(non_snake_case)] pub isPrimop: bool, + #[allow(non_snake_case)] + pub isFunctor: Option, pub name: Option, pub position: Option, pub args: Option>, diff --git a/website/src/app/f/[...path]/page.tsx b/website/src/app/f/[...path]/page.tsx index 85c140f..626244c 100644 --- a/website/src/app/f/[...path]/page.tsx +++ b/website/src/app/f/[...path]/page.tsx @@ -203,7 +203,7 @@ export default async function Page(props: { params: { path: string[] } }) { idx === all.length - 1 ? ( <> - + {attr} @@ -251,6 +251,13 @@ export default async function Page(props: { params: { path: string[] } }) { )} )} + {meta?.is_functor && ( + + )} @@ -304,7 +311,9 @@ export default async function Page(props: { params: { path: string[] } }) { {meta && }
- {(!!meta?.aliases?.length || (!!signature && !meta?.signature)) && ( + {(!!meta?.aliases?.length || + (!!signature && !meta?.signature) || + meta?.is_functor) && ( <> )} + {meta?.is_functor && ( + <> + + This is a Functor + +
+ Learn about functors + + )} {!!meta?.aliases?.length && ( <> The lambda function can access the attributes of the attribute set often called `self`. + +## Examples of Functors + +In the following example, `build` is both an attribute set and a lambda. +`__functor` is a reserved attribute name that turns the attribute set `build` into a **functor**. + +```nix +{ + build = { + foo = 1; + __functor = self: _arg: self.foo; + }; +} +``` + +The attribute set `build` can now be used as a lambda via `function application`. + +```nix +nix-repl> build "linux" +=> 1 +``` + +But at the same time `build` is still an Attribute set. + +```nix +nix-repl> build +=> { __functor = «lambda»; foo = 1; } +``` + +## Applications of Functors + +Functors find practical applications including some in nixpkgs: + +- `lib.makeOverridable` +- `lib.setFunctionArgs` +- `lib.mirrorFunctionArgs` +- `lib.types` and `lib.options` +- and many more. + +## When to use a functor + +(Noogle opinion) + +The use of functor should be avoided and is often **unnecessary**. + +Using them can add up in **complexity** and makes is hard to learn and maintain a certain piece of code. diff --git a/website/src/components/PositionLink.tsx b/website/src/components/PositionLink.tsx index 72e1bdf..fb7d7a9 100644 --- a/website/src/components/PositionLink.tsx +++ b/website/src/components/PositionLink.tsx @@ -86,28 +86,52 @@ export const PositionLink = ({ )} - {!contentPosition && position && ( + {!contentPosition && ( - - - + + + )} + {lambda_position && ( + + + + )} {!!count_applied && count_applied > 0 && (