Skip to content

Commit

Permalink
init tuto-2 and tweak pages
Browse files Browse the repository at this point in the history
  • Loading branch information
viperML committed Sep 16, 2024
1 parent e014e47 commit 6088e11
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 17 deletions.
2 changes: 0 additions & 2 deletions src/components/Centered.astro
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---
---

<section class="flex flex-col main-container">
<slot />
</section>
142 changes: 132 additions & 10 deletions src/content/blog/nix-tuto-1/index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
title: "The Nix Lectures, part 1: Language basics"
title: "The Nix lectures, part 1: Language basics"
pubDate: 2024-09-11T07:32:26Z
draft: true
summary: FIXME
summary: |
Covering the fundamentals such as the syntax of Nix as a expression-based
language, the types, and some functions from builtins and lib.
slug: nix-tuto-1
---

Expand Down Expand Up @@ -60,7 +62,7 @@ blocks:

Nix is a dynamically typed language, that is, you don't write the type of a
value. Rather it is calculated at runtime. Nix is also a very simple language,
and it has very few types.
and it has very few types.

> [!NOTE]
> As one of the key objectives of Nix is software reproducibility, it needs a
Expand Down Expand Up @@ -242,7 +244,7 @@ To apply a function, you use a single space:

> [!IMPORTANT]
> Functions take a **single** argument, and return a **single** value from its
body.
body.

While this might seem like a limitation, you can write functions that take
multiple values in two ways. One way, is to take an attrset as an argument, which then you can decompose:
Expand Down Expand Up @@ -303,13 +305,24 @@ functions, is that Nix displays them as `lambda`:
#=> 3
```

> [!TIP]
> Both functions and lists are aware of spaces: functions use space for
> application, and lists are space-separated. Lists take precedence over
> functions, so nbe careful when applying functions inside lists:
> ```nix
> [
> f 1 # two elements
> (f 1) # the result of the function
> ]
> ```
## Compound expressions

One of the key insights to understanding Nix's syntax, is that everything is an
expression. Expressions can be nested into another. Unlike a regular language,
there are no "statements" in Nix.

An statement would imply multiple values. In contrast, in Nix we have a single
An statement would imply multiple values. In contrast, in Nix we have a single
expression, that evaluates from the bottom.

One way to visualize this, is by adding parenthesis for every expression:
Expand Down Expand Up @@ -350,7 +363,7 @@ as return values of functions or anything more or less complex:
```nix
{
config = if useCuda then "cuda" else "noCuda";
# using parenthesis to visualize the expressions
config' = (if (useCuda) then ("cuda") else ("noCuda");
}
Expand Down Expand Up @@ -435,14 +448,14 @@ mkShell {

In this section, we will cover some functions from `builtins`. This is the name
of a variable that is provided by Nix itself. I will cover some of the most used
ones. You can check the documentation for the rest in the
ones. You can check the documentation for the rest in the
[Nix manual](https://nix.dev/manual/nix/2.18/language/builtins).

### import

`import` allows you to load a nix expression from another file. Remember that
nix is an expression-based language, so a file always contains a single
expressions that returns a single value.
nix is an expression-based language, so a file always contains a single expression
that returns a single value.

```nix
# foo.nix
Expand Down Expand Up @@ -523,7 +536,7 @@ builtins.filter (x: builtins.stringLength x > 1) ["f" "bar"]

These are the equivalent to `map` and `filter`, but can be applied to
attrsets instead of lists. They take a *curried* functions, for the name and
value.
value.

```nix
builtins.mapAttrs (name: value: "${name}:${value}") { foo = "bar"; }
Expand Down Expand Up @@ -714,3 +727,112 @@ lib.makeLibraryPath [ pkgs.hello pkgs.coreutils ]
#=> "/nix/store/yb84nwgvixzi9sx9nxssq581pc0cc8p3-hello-2.12.1/lib:/nix/store/0kg70swgpg45ipcz3pr2siidq9fn6d77-coreutils-9.5/lib"
```

## Advanced topics

Finally, I want to mention some advanced topics that are part of the base
language. If you are just getting started with Nix, you might not need to know
about this. Or you might want to read ahead because of curiosity.

### Merging attribute sets

I decided to skip the merging operator `//` when talking about attrsets. It
takes two attrsets, and inserts the keys from the right-hand side into the
left-hand side. Notice that this definition is very specific to what it does: **it
does not try to merge any child attrsets**. Looking at an example:

```nix
{ a = "avalue"; b = { ba = "bavalue"; bc = "bcvalue"; }; } // { b = 2; c = "cvalue"; }
#=> {
# a = "avalue";
# b = 2;
# c = "cvalue";
# }
```

Notice that `c` was inserted, but also `b`, removing the nested attrset from the
left. This can have negative consequences, as naively using `//` can lead to
problems.

Merging attrsets is not something that you will do commonly. A prefered approach
would be to create a new attrset, and explicitly listing the key-value pairs
with `inherit`.

```nix
let c = "avalue"; b = { ba = "bavalue"; }; in { b = { inherit c; inherit (b) ba; }; }
#=> {
# b = {
# ba = "bavalue";
# c = "avalue";
# };
# }
```

### Recursion

You can achieve complex behavior by using the different mechanism for recursion
in nix.

One way is using `let-in`:

```nix
let
x = [ x ];
in
x
```

Because `x` itself is in scope, you can create the list that contains itself
(forever). Of course, this is an useless example, but there are other patterns
that are best (or only) implemented with recursion.

The nixpkgs' lib also offers `lib.fix`, which has a simple definition:

```nix
fix =
f:
let
x = f x;
in
x;
```

Fix takes a function, and applies the function using the **result** of the
function as its argument. Trippy, eh? This allows us to write very compact
self-referencing values:

```nix
lib.fix (self: { a = 1; b = self.a + 1; })
#=> {
# a = 1;
# b = 2;
# }
```

Nix also provides the `rec` keyword that can be prefixed to attsets, and
provides a similar experience to `fix`, but with an implicit scope similar to
let-in:

```nix
rec { a = 1; b = a + 1; }
#=> {
# a = 1;
# b = 2;
# }
```

I prefer to use `fix`, because it is more explicit about where things come --
you have to mention your function argument `self`, but you can choose whichever
you want.


### Interpolating attrsets into strings


## Finale

I hope you got a broad idea about how Nix itself works. It is a very simple
language at is core, and most of the abstractions are implemented in Nixpkgs.

When looking at complex Nix, always remember to try to pull apart the basic
expressions. After all, everything is mostly functions and attrsets/lists.

11 changes: 11 additions & 0 deletions src/content/blog/nix-tuto-2/derivation.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# derivation.nix
builtins.derivation {
system = "x86_64-linux";
name = "sample";
builder = "/bin/sh";

args = [
"-c"
"echo hello > $out"
];
}
50 changes: 50 additions & 0 deletions src/content/blog/nix-tuto-2/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: "The Nix lectures, part 2: Derivations"
pubDate: 2024-09-16T07:07:01Z
draft: true
summary: FIXME
slug: nix-tuto-2
---

This is part 2 of a tutorial series that covers all of Nix. We covered
language basics in the first part, and in this post we will cover derivations.

## builtins.derivation

This is the primitive that Nix uses to define derivations. You will probably
**never** use it, as we rely on higher-level abstractions that are built on top
of `builtins.derivation`.

> [!NOTE]
> Remember that the design of the Nix language, it is a very simple language.
> Having a small API surface means that you don't really have to update the API
> as time passes, but rather the abstractions that are built on top. This is
> useful when you want to use a current Nix binary to evaluate some nixpkgs from
> *many* years ago.
`builtins.derivation` is a function that takes an attrset with some
**known keys**, and the rest are set as environment variables. A simple
invocation might look like this:

```nix file: "derivation.nix"
#=> «derivation /nix/store/n34150nf03sh04j8mjzm8sawdqx9sgqi-sample.drv»
```

Nix will tell use that the return type is a "derivation". However, from chapter
1, I didn't mention that this type exists. What is happening here is that
derivations are implemented as **attrsets** with a special field called `type`.

```nix
{ type = "derivation"; }
#=> «derivation»
```

> [!TIP]
> One of the key insights is that **derivations** are **attrsets**, meaning we
> can access fields from them. You will often see things like
> `pkgs.hello.overrideAttrs`, etc.
Derivations can be built in the repl with `:b`, or with the command `nix build
-f derivation.nix`.

As you might remember, you can do string interpolation with attrsets:
9 changes: 9 additions & 0 deletions src/content/blog/nix-tuto-3/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: "The Nix lectures, part 3: Module system"
pubDate: 2024-09-16T07:08:21Z
draft: true
summary: FIXME
slug: nix-tuto-3
---

## hello
9 changes: 9 additions & 0 deletions src/content/blog/nix-tuto-4/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: "The Nix lectures, part 4: Integration"
pubDate: 2024-09-16T07:09:05Z
draft: true
summary: FIXME
slug: nix-tuto-4
---

## hello
24 changes: 24 additions & 0 deletions src/pages/404.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
import Base from "../layouts/Base.astro";
import Centered from "../components/Centered.astro";
---

<Base
seo={{
title: "Page not found",
}}
>
<Centered>
<h1>404</h1>
<h3>Not found</h3>
</Centered>
</Base>

<style>
.test {
box-sizing: border-box;
padding: 10px;
border: 4px solid rgb(68, 68, 68);
border-radius: 10px;
}
</style>
32 changes: 28 additions & 4 deletions src/pages/experiments/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,40 @@ import Base from "../../layouts/Base.astro";
import Centered from "../../components/Centered.astro";
import { highlight } from "neohome-rs";
const webringSite = "ayats";
const webringURL = "https://nixwebr.ing"
---

<Base
seo={{
title: "Experiments",
}}
>
<Centered>
<h2>Coming soon...</h2>
</Centered>
<Centered class:list={["test"]}>
<div class="test">
<h2>Nix Webring</h2>

<ul class="webring-control">
<li><a href=`${webringSite}/prev/${webringURL}`>&lt;&lt;</a></li>
<li><a href={webringSite}>Home Page</a></li>
<li><a href=`${webringSite}/random`>Random</a></li>
<li><a href=`${webringSite}/next/${webringURL}`>&gt;&gt;</a></li>
</ul>


{highlight("let x = 1", "javascript")}
{highlight("let x = 1", "javascript")}

</div>
</Centered>
</Base>


<style>
.test {
box-sizing: border-box;
padding: 10px;
border: 4px solid rgb(68, 68, 68);
border-radius: 10px;
}
</style>
3 changes: 2 additions & 1 deletion src/remark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export const remarkCodeMeta: Plugin<[], Root> = () => {

const res = fs.readFileSync(final, {encoding: "utf-8"});

node.value += "\n" + res.trim();
// node.value += "\n" + res.trim();
node.value = res.trim() + "\n" + node.value
}
});
}
Expand Down

0 comments on commit 6088e11

Please sign in to comment.