"One file tells you everything."
Lucid is an intermediate language designed for AI-generated code. Unlike existing languages built for human readability, Lucid is designed with one priority — eliminating any room for AI to guess.
The reason AI gets code wrong isn't because syntax is complex. There's one root cause — it doesn't know.
# A situation where AI gets confused
result = some_lib.process(data, True, None, 3)
# What is True, what is 3, what does it return — all guessworkAI cannot reliably know the function signatures of externally included files. APIs differ across versions, deprecated functions get used, argument orders get mixed up.
Lucid solves this at the language level.
Opening a Lucid file gives you the name, parameters, return type, and side effects of every function — without ever looking at another file.
extern math.add :: (a:Int, b:Int) -> Int !pure
extern io.write :: (path:Str, data:Str) -> () !io
fn main :: () -> () = {
let x:Int = math.add(1, 2)
io.write("out.txt", "hello")
}
What you can tell from this file alone:
math.addtakes twoInts, returns anInt, and is a pure functionio.writetouches the filesystem (!io)xis of typeInt
extern is the core of Lucid.
When using a function from another file, declare its full specification at the top of the current file.
extern module.function :: (param:Type, ...) -> ReturnType [!attribute]
| Attribute | Meaning |
|---|---|
!pure |
Pure function — no side effects |
!io |
Performs I/O (file, network, etc.) |
!mut |
Mutates state |
@deprecated("2.0") |
No longer recommended |
You don't have to write extern declarations by hand.
The lucid include command generates them automatically.
lucid include math.lcd --target main.lcdParses math.lcd and injects the externs into the top of main.lcd automatically.
// extern from math.lcd
extern math.add :: (a:Int, b:Int) -> Int !pure
extern math.multiply :: (a:Int, b:Int) -> Int !pure
Declaring every function as extern makes files very long. Simple functions like those in the std library don't need to be extern'd one by one.
Adding #![no_extern] embeds all information directly in the function name.
#![no_extern]
fn add :: (a:Int, b:Int) -> Int !pure = a + b
The compiler automatically transforms the name:
add$a_Int$b_Int$to_Int
The name alone tells you the arguments and return type, so no extern declaration is needed. If a function name exceeds 60 characters, the compiler warns you — suggesting you use extern instead.
To use a no_extern function from another file, reference it with include:
include math
fn main :: () -> () = {
let x:Int = add$a_Int$b_Int$to_Int(1, 2)
}
// Function definition
fn name :: (param:Type, ...) -> ReturnType [!attribute] = expression
// Variable binding
let name:Type = expression
// Conditional
condition ? then_expr : else_expr
// Lambda
lambda (param:Type, ...) -> expression
// Loop
loop (init, condition, step) { expression }
// Block (last expression is the return value)
{
let x:Int = ...
x
}
Int, Float, Str, Bool // Primitive types
() // Unit (void)
Int[] // Array
(Int, Str) // Tuple
Result<Int, Err> // Result type
(Int, Int) -> Bool // Function type
fn fib :: (n:Int) -> Int !pure =
n <= 1 ? n : fib(n-1) + fib(n-2)
extern io.read :: (path:Str) -> Str !io
extern str.lines :: (s:Str) -> Str[] !pure
extern arr.len :: (arr:Str[]) -> Int !pure
fn count_lines :: (path:Str) -> Int !io = {
let content:Str = io.read(path)
let lines:Str[] = str.lines(content)
arr.len(lines)
}
extern arr.filter :: (arr:Int[], pred:Int -> Bool) -> Int[] !pure
extern arr.fold :: (arr:Int[], init:Int, f:(Int,Int) -> Int) -> Int !pure
#![no_extern]
fn is_even :: (n:Int) -> Bool !pure = n % 2 == 0
fn sum_evens :: (arr:Int[]) -> Int !pure = {
let evens:Int[] = arr.filter(arr, is_even$n_Int$to_Bool)
arr.fold(evens, 0, lambda (acc:Int, x:Int) -> acc + x)
}
lucid build main.lcd --output out.c # Compile to C
lucid check main.lcd # Type check only
lucid include math.lcd --target main.lcd # Auto-generate externs
lucid query math.lcd add # Look up a function definition
lucid lsp # Start LSP serverLucid's package manager is Prism.
prism install math
prism build
prism publish# prism.toml
[package]
name = "myproject"
version = "0.1.0"
[dependencies]
math = "0.1.0"
io = "0.1.0"