A pipe-oriented functional programming language with a tree-walk interpreter written in TypeScript.
let numbers = [1, 2, 3, 4, 5]
let sumOfSquares = numbers
/> filter((x) -> x > 2)
/> map((x) -> x * x)
/> reduce(0, (acc, x) -> acc + x)
sumOfSquares /> print -- 50
- Pipes — Left-to-right data flow with
/>operator - Functions — First-class, with optional type annotations
- Decorators — Composable function modifiers (
#log,#memo,#retry(3)) - Records — Object literals with member access
- Contexts — Dependency injection system
- Async/Await — Promise-based asynchronous execution
# Clone and install
git clone https://github.com/mcclowes/lea.git
cd lea
npm install
# Run a file
npm run lea example.lea
# Interactive REPL
npm run repllet x = 10 -- Immutable
maybe counter = 0 -- Mutable
let double = (x) -> x * 2
let add = (a, b) -> a + b
-- Type annotations (trailing :: syntax)
let typed = (x) -> x * 2 :: Int :> Int
-- Multi-statement bodies
let process = (x) ->
let y = x * 2
let z = y + 1
z
Enable strict type checking with #strict pragma or --strict CLI flag:
#strict
let add = (a, b) -> a + b :: (Int, Int) :> Int
add(5, 10) -- OK: 15
add("a", "b") -- Error: Argument 'a' expected Int, got string
npm run lea file.lea --strict16 /> sqrt -- sqrt(16) = 4
5 /> add(3) -- add(5, 3) — value becomes first arg
5 /> add(3, _) -- add(3, 5) — placeholder controls position
-- Chain operations
[1, 2, 3, 4, 5]
/> filter((x) -> x > 2)
/> map((x) -> x * x)
/> print
let user = { name: "Max", age: 99 }
user.name /> print -- "Max"
let nested = { data: { value: 42 } }
nested.data.value /> print
Apply modifiers after function body:
let logged = (x) -> x * 2 #log
let cached = (x) -> expensiveOp(x) #memo
let resilient = (x) -> riskyOp(x) #retry(3) #timeout(1000)
Available decorators:
#log— Log inputs/outputs#memo— Cache results#time— Log execution time#retry(n)— Retry on failure#timeout(ms)— Fail if exceeds time#validate— Runtime type checking#pure— Warn on side effects#async— Mark as async#trace— Deep call logging
Dependency injection for functions:
-- Define context with default
context Logger = { log: (msg) -> print("[DEFAULT] " ++ msg) }
-- Override in scope
provide Logger { log: (msg) -> print("[PROD] " ++ msg) }
-- Attach to function
let greet = (name) ->
@Logger
Logger.log("Hello " ++ name)
"World" /> greet -- "[PROD] Hello World"
let fetchData = () -> delay(100) #async
await fetchData() /> print
- Syntax Guide — Complete language syntax reference
- Built-in Functions — All built-in functions and decorators
- Pipelines — First-class pipelines, composition, and algebra
- Concurrency — Async/await, parallel pipes, and concurrent execution
This repository includes Claude Code skills to help you write and modify Lea code effectively.
| Skill | Description |
|---|---|
lea |
Use when writing or modifying Lea code — covers syntax, builtins, and interpreter internals |
functional-programming |
Use when writing functional code — covers FP best practices (Lea differs from traditional FP languages) |
OpenSkills is a CLI tool that makes it easy to install and manage Claude Code skills.
Step 1: Install OpenSkills
npm i -g openskillsStep 2: Install Skills
# Interactive mode
openskills install mcclowes/lea
# Or skip prompts
openskills install mcclowes/lea -yStep 3: Sync (optional)
If you have an AGENTS.md file, sync to update configuration:
openskills sync- Project-level (default):
openskills install mcclowes/lea— installs to.claude/skills/ - Global:
openskills install mcclowes/lea -g— installs to~/.claude/skills/
Source → Lexer → Tokens → Parser → AST → Interpreter → Result
src/lexer.ts— Tokenizationsrc/parser.ts— Recursive descent parsersrc/ast.ts— AST node definitionssrc/interpreter.ts— Tree-walk interpretersrc/repl.ts— Interactive REPL
MIT