add proper handling for functors

This commit is contained in:
Johannes Kirschbauer 2024-01-07 16:42:51 +01:00 committed by Johannes Kirschbauer
parent a8a7a0e88e
commit 54530f99e6
9 changed files with 149 additions and 28 deletions

View File

@ -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": {

View File

@ -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; };

View File

@ -122,6 +122,9 @@ pub fn categorize(data: &Vec<Docs>) -> 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<Docs>, 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 {

View File

@ -171,6 +171,8 @@ struct DocumentFrontmatter<'a> {
/// If an item is primop then it should have the PrimopMeta field.
is_primop: Option<bool>,
primop_meta: Option<PrimopMatter<'a>>,
/// is functor
is_functor: Option<bool>,
/// 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,

View File

@ -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<bool>,
pub name: Option<String>,
pub position: Option<FilePosition>,
pub args: Option<Vec<String>>,

View File

@ -203,7 +203,7 @@ export default async function Page(props: { params: { path: string[] } }) {
idx === all.length - 1 ? (
<>
<meta key={idx} data-pagefind-meta={`name:${attr}`} />
<Box component="h3" sx={{ display: "none" }}>
<Box key={idx * 2} component="h3" sx={{ display: "none" }}>
{attr}
</Box>
</>
@ -251,6 +251,13 @@ export default async function Page(props: { params: { path: string[] } }) {
)}
</>
)}
{meta?.is_functor && (
<Chip
label={"Functor"}
color="warning"
sx={{ ml: "auto", maxWidth: "10rem" }}
/>
)}
<ShareButton />
</Box>
<Divider flexItem sx={{ mt: 2 }} />
@ -304,7 +311,9 @@ export default async function Page(props: { params: { path: string[] } }) {
<MDX source={source} />
{meta && <PositionLink meta={meta} content={item?.content} />}
<div data-pagefind-ignore="all">
{(!!meta?.aliases?.length || (!!signature && !meta?.signature)) && (
{(!!meta?.aliases?.length ||
(!!signature && !meta?.signature) ||
meta?.is_functor) && (
<>
<Divider flexItem />
<Typography
@ -321,6 +330,15 @@ export default async function Page(props: { params: { path: string[] } }) {
</Typography>
</>
)}
{meta?.is_functor && (
<>
<Typography variant="h5" component={"div"}>
This is a Functor
</Typography>
<br />
<Link href="/md/tutorials/functors">Learn about functors</Link>
</>
)}
{!!meta?.aliases?.length && (
<>
<Typography

View File

@ -0,0 +1,60 @@
# Functors in nix
**Functors combine functions and attribute sets**
## Understanding Functors
A functor is a polymorphic data type that can represent both a
**lambda function** (`x: x`) and an **attribute set** (`{ key = value; }`).
This means that the Atribute set can hold data in the form of attributes.
And also be a lambda function that can operate on that data.
-> 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.

View File

@ -86,28 +86,52 @@ export const PositionLink = ({
</Button>
</Link>
)}
{!contentPosition && position && (
{!contentPosition && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
target="_blank"
href={getSourcePosition(
"https://github.com/hsjobeki/nixpkgs/tree/migrate-doc-comments",
position
)}
>
<Button
data-pagefind-ignore="all"
variant="text"
sx={{
textTransform: "none",
my: 1,
placeSelf: "start",
}}
startIcon={<LinkIcon />}
{attr_position && (
<Link
target="_blank"
href={getSourcePosition(
"https://github.com/hsjobeki/nixpkgs/tree/migrate-doc-comments",
attr_position
)}
>
Underlying function
</Button>
</Link>
<Button
data-pagefind-ignore="all"
variant="text"
sx={{
textTransform: "none",
my: 1,
placeSelf: "start",
}}
startIcon={<LinkIcon />}
>
Attribute position
</Button>
</Link>
)}
{lambda_position && (
<Link
target="_blank"
href={getSourcePosition(
"https://github.com/hsjobeki/nixpkgs/tree/migrate-doc-comments",
lambda_position
)}
>
<Button
data-pagefind-ignore="all"
variant="text"
sx={{
textTransform: "none",
my: 1,
placeSelf: "start",
}}
startIcon={<LinkIcon />}
>
Underlying function
</Button>
</Link>
)}
{!!count_applied && count_applied > 0 && (
<Typography
variant="subtitle2"

View File

@ -35,6 +35,7 @@ export type DocMeta = {
path: ValuePath;
aliases?: ValuePath[];
is_primop?: boolean;
is_functor?: boolean;
primop_meta?: PrimopMatter;
attr_position?: FilePosition;
lambda_position?: FilePosition;