Nix
2003fragletMCP + fragletc
Nix is a purely functional package manager and build system that treats packages like values in purely functional programming languages such as Haskell - they are built by functions that don't have side-effects, and they never change after they have been built.
Language Overview
Nix is both a package manager and a domain-specific functional programming language used for package management and system configuration. The Nix language is dynamically typed and purely functional, making it excellent for describing package builds and system configurations in a declarative way.
Hello World Explanation
Our Nix "Hello World!" program uses a pure Nix expression that gets evaluated natively:
hello-world.nix:
# Pure Nix Hello World program
# This is a native Nix expression that evaluates to "Hello World!"
"Hello World!"hello-world.sh:
#!/bin/sh
# Source Nix environment
. /home/human/.nix-profile/etc/profile.d/nix.sh
# Evaluate the pure Nix expression and clean up the output
nix-instantiate --eval --strict /hello-world/hello-world.nix | tr -d '"'This approach demonstrates Nix's core functionality as a language - we write a pure Nix expression (just the string literal "Hello World!") and use nix-instantiate --eval to evaluate it. This is truly native Nix, not just using Nix to spawn other tools.
Unique Features
- Purely Functional: Package definitions are written as functions with no side effects
- Reproducible Builds: Identical inputs always produce identical outputs
- Atomic Upgrades and Rollbacks: System changes are atomic and can be rolled back
- Multiple Versions: Different versions of packages can coexist without conflicts
- Lazy Evaluation: Expressions are only evaluated when needed
- Immutable Store: Packages are stored in an immutable way using cryptographic hashes
MUSL Compatibility
Nix works excellently on MUSL-based systems like Alpine Linux through its single-user installation mode. The --no-daemon flag ensures compatibility with containerized environments and MUSL-based distributions.
Beyond Hello World
You can easily modify the hello-world.nix file to explore more complex Nix capabilities:
Complex Nix Expressions
# Mathematical expression
1 + 2 * 3
# String interpolation
let name = "World"; in "Hello ${name}!"
# Function application
(x: "Hello " + x) "World!"
# List operations
builtins.head ["Hello" "World" "!"]Using Nix for Package Management
You can also modify the shell script to use nix-shell for creating reproducible environments:
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p figlet cowsay
figlet "Hello from Nix!"
cowsay "Reproducible environments are awesome!"Pure Nix Expression Evaluation
The beauty of this approach is that hello-world.nix contains pure Nix language code that can be arbitrarily complex, while the shell script simply evaluates it using native Nix tools.
Further Exploration
- Try the Nix Pills tutorial series
- Explore NixOS - a Linux distribution built around Nix
- Learn about Flakes - Nix's modern package management system
- Experiment with more complex Nix expressions in the
.nixfile
Hello World
# Pure Nix Hello World program
# This is a native Nix expression that evaluates to "Hello World!"
# BEGIN_FRAGLET
let name = "World"; in "Hello ${name}!"
# END_FRAGLETCoding Guide
Language Version
Nix 2.x (latest stable)
Execution Model
- Functional, lazy evaluation language
- Pure expressions that evaluate to values
- Code is evaluated using
nix-instantiate --eval --strict - Expressions are evaluated in a pure functional context
- No side effects during evaluation (pure functional)
Key Characteristics
- Functional programming language
- Lazy evaluation
- Immutable data structures
- Strong typing with type inference
- Pattern matching with
let ... inexpressions - String interpolation with
${variable} - Lists:
[1 2 3](space-separated, no commas) - Attribute sets (maps):
{ a = 1; b = 2; } - Functions:
x: x + 1(lambda syntax) - Case-sensitive
- Whitespace-sensitive (indentation matters in some contexts)
Fragment Authoring
Write valid Nix expressions that evaluate to a string (or any printable value). Your fragment becomes the expression body. You can write:
- Simple string literals:
"Hello, World!" - Let expressions:
let x = "World"; in "Hello ${x}!" - Function calls:
builtins.toString 42 - List operations:
builtins.concatStringsSep ", " ["a" "b" "c"] - Attribute set operations:
{ a = 1; b = 2; }.a
The expression will be evaluated and the result printed (with quotes removed).
Available Builtins
Nix provides many built-in functions:
builtins.toString- Convert value to stringbuiltins.concatStringsSep- Join strings with separatorbuiltins.length- Get length of listbuiltins.head- Get first element of listbuiltins.tail- Get all but first elementbuiltins.elemAt- Get element at indexbuiltins.map- Map function over listbuiltins.filter- Filter list with predicatebuiltins.foldl'- Left foldbuiltins.readFile- Read file (in impure contexts)builtins.pathExists- Check if path exists- And many more (see Nix manual)
Common Patterns
- String literal:
"Hello, World!" - String interpolation:
"Hello ${name}!" - Let binding:
let x = 5; in x + 10 - Lists:
[1 2 3 4 5](space-separated) - List operations:
builtins.map (x: x * 2) [1 2 3] - Attribute sets:
{ name = "Alice"; age = 30; } - Attribute access:
attrs.nameorattrs."name" - Functions:
x: x + 1(anonymous) orf: x: f x(higher-order) - Conditionals:
if condition then value1 else value2 - Recursive attribute sets:
rec { a = 1; b = a + 1; }
Examples
# Simple string output
"Hello from fragment!"
# String interpolation with let binding
let name = "Nix"; in "Hello ${name}!"
# Calculations
let a = 5; b = 10; in builtins.toString (a + b)
# List operations
builtins.concatStringsSep ", " ["apple" "banana" "cherry"]
# List mapping
builtins.concatStringsSep " " (builtins.map (x: builtins.toString (x * 2)) [1 2 3 4 5])
# Attribute sets
let person = { name = "Alice"; age = 30; }; in "${person.name} is ${builtins.toString person.age} years old"
# Functions
let add = a: b: a + b; in builtins.toString (add 5 10)
# Conditional expressions
if true then "Yes" else "No"
# Recursive attribute sets
let attrs = rec { a = 1; b = a + 1; c = a + b; }; in builtins.toString attrs.cCaveats
- Nix is a pure functional language - no side effects during evaluation
- All values are immutable
- Lazy evaluation means expressions are only evaluated when needed
- String interpolation requires
${}syntax - Lists are homogeneous (all elements same type)
- Attribute sets use
;as separator, not, - Functions are curried:
f: x: y: f x yis equivalent tof: (x: (y: f x y)) - The expression must evaluate to a printable value (string, number, etc.)
- Complex data structures will be printed in Nix syntax, not as plain text
nix-instantiate --eval --strictevaluates strictly (no lazy evaluation delays)
Fraglet Scripts
Echo Args
#!/usr/bin/env -S fragletc --vein=nix
builtins.concatStringsSep " " (builtins.tail builtins.getEnv "ARGS")Stdin Upper
#!/usr/bin/env -S fragletc --vein=nix
builtins.getEnv "STDIN_LINE" or ""Test
#!/usr/bin/env -S fragletc --vein=nix
let name = "World"; in "Hello ${name}!"