π Documentation
Comprehensive documentation for the Nixi programming language.
π Introduction to Nixi
Nixi is a hybrid programming language designed to bridge the gap between functional programming, system scripting, and modern web development. Inspired by Nix's pure functional approach, Bash's practical built-in utilities, and React.js's component-based UI paradigm, Nixi offers a unique blend of declarative syntax, imperative operations, and declarative styling.
Design Principles
- Functional Core: Everything is an expression, with immutable data and pure functions as foundation. No side effects in core logicβchanges are explicit and predictable.
- Practical Imperative Layer: Built-in functions for system interactions (file I/O, directory operations) allow seamless scripting without sacrificing purity.
- Declarative UI: GUI components are defined declaratively, with CSS styling integrated directly into the language for a cohesive development experience.
- Compilation to Web: Nixi programs compile to standalone HTML files, making them portable and runnable in any browser.
Key Principles
- Immutability: Data structures are immutable by default, promoting safer, more predictable code.
- Composition over Inheritance: Functions and components are composed rather than extended, leading to modular, reusable code.
- Zero Dependencies: Compiled output is pure HTML/CSS/JS, requiring no runtime dependencies.
- Developer Experience: Syntax highlighting, error messages, and editor support make development intuitive.
Execution Model
Nixi code is interpreted and compiled in two phases:
- Interpretation: The AST is evaluated, executing functions and building component trees.
- Compilation: The result is rendered to HTML with embedded CSS and JavaScript for interactivity.
To execute Nixi code:
- Ensure Node.js is installed.
- Clone Nixi repository:
git clone https://github.com/ijadux2/nixi.git && cd nixi - Install dependencies:
npm install - Run a .nixi file:
node src/cli.js path/to/your/file.nixi - The output will be an HTML file (e.g., if your code uses
saveHTML, it generates the specified file).
Example: node src/cli.js examples/simple-gui.nixi will generate simple-gui.html, which you can open in a browser.
π§ Core Syntax
Nixi's syntax is inspired by Nix, using let expressions for bindings and in to define scope.
Let-In Expressions
Bindings are defined in a let block and used in in expression:
let x = 5; y = x + 3; in y # Returns 8
Data Types
- Strings: Double or single quotes:
"hello"or'world'. Concatenation with+. - Numbers: Integers/floats:
42,3.14. Support arithmetic operators. - Booleans:
true,false. Used in conditionals. - Lists: Arrays:
[1, 2, 3]. Access withhead,tail,length. - Records: Objects:
{ name: "Alice"; age: 30 }. Access with dot notation or destructuring. - Null:
nullfor absence of value.
Operators
- Arithmetic:
+,-,*,/,% - Comparison:
==,!=,<,>,<=,>= - Logical:
&&,||,! - List:
++for concatenation
Comments
Single-line comments start with #:
# This is a comment let x = 10; in x
π§ Functions and Expressions
Functions are first-class citizens, supporting currying, lambdas, and higher-order patterns.
Function Definitions
Curried functions:
let add = x: y: x + y; increment = add 1; # Partially applied in increment 5 # Returns 6
Lambda Expressions
Anonymous functions:
let square = x: x * x; numbers = [1, 2, 3]; squares = map square numbers; in squares # [1, 4, 9]
Higher-Order Functions
Functions taking/returning functions:
let applyTwice = f: x: f (f x); double = x: x * 2; in applyTwice double 3 # Returns 12
Pattern Matching and Records
Destructure records in parameters:
let greet = { name, age }: "Hello " + name + ", age " + toString(age) + " years old"; in greet { name: "Alice"; age: 25 }
Recursion
For loops and iteration:
let factorial = n: if n <= 1 then 1 else n * factorial (n - 1); in factorial 5 # Returns 120
π§ Built-in Functions
Nixi provides built-in functions for system operations, data manipulation, and utilities.
System Operations
ls(path): List directory contents.cd(path): Change directory.pwd(): Get current directory.echo(str): Print to console.readFile(path): Read file contents.writeFile(path, content): Write to file.fileExists(path): Check if file exists.
String Operations
toUpperCase(str): Convert to uppercase.toLowerCase(str): Convert to lowercase.length(str): String length.substring(str, start, end): Extract substring.indexOf(str, substr): Find substring position.
Mathematical Operations
add(a, b),subtract(a, b),multiply(a, b),divide(a, b)sqrt(n): Square root.pow(base, exp): Power.abs(n): Absolute value.
List Operations
head(list): First element.tail(list): All but first.length(list): List length.map(f, list): Apply function to each element.filter(pred, list): Filter elements.fold(f, init, list): Reduce list.
Conversion and Utilities
toString(val): Convert to string.toNumber(str): Convert to number.typeOf(val): Get type as string.
Example Usage
let files = ls("."); upperFiles = map toUpperCase files; in echo ("Files: " + toString(upperFiles))
π¨ GUI and Styling
Nixi supports declarative GUI components with integrated CSS styling.
Component Declaration
Components are HTML-like elements:
let Button = button { text: "Click Me"; class: "btn"; onClick: echo "Clicked!" }; in Button
Props and Children
Components accept properties and nested children:
let Card = { title, content }: div { class: "card"; children: [ h3 { text: title }; p { text: content } ] }; in Card { title: "Hello"; content: "World" }
CSS Styling
Use style keyword for CSS:
style "btn" { background: "blue"; color: "white"; padding: "10px 20px"; border-radius: "6px"; }
Event Handlers
Attach functions to events:
input { onChange: value: echo ("Input: " + value) }
Composition Patterns
Build complex UIs by composing components:
let App = div { class: "app"; children: [ header { text: "App Header" }; main { children: [Button; Card] } ] }; in saveHTML(App, "app.html", "My App")
π§ Control Flow and Error Handling
Nixi uses expressions for control flow and Result types for errors.
Conditionals
let sign = n: if n > 0 then "positive" else if n < 0 then "negative" else "zero"; in sign -5 # "negative"
Error Handling with Results
let Result = { success, value, error }; safeDivide = a: b: if b == 0 then Result { success: false; error: "Division by zero" } else Result { success: true; error: null; value: a / b }; in safeDivide 10 0
Debugging
Use echo for logging:
let debug = msg: val: let _ = echo ("[DEBUG] " + msg + ": " + toString(val)); in val; in debug "Starting..." (1 + 2)
π§ Advanced Topics
Nixi supports importing other .nixi files (future feature; currently, compose in single files).
Performance Tips
- Use tail recursion for large iterations.
- Avoid deep nesting; compose functions.
- Lazy evaluation for large data structures.
Integration with JavaScript
Compiled output includes JS for events; extend with custom scripts.
- Immutability: Never mutate data; return new values.
- Pure Functions: Functions should depend only on inputs.
- Component Reusability: Parameterize components for flexibility.
- Error Boundaries: Wrap risky operations in Result types.
- Testing: Test functions with various inputs; use assertions.