This project is an intent-first, constrained general-purpose functional language with a small compiler pipeline: lex/parse -> validate -> lower -> eval. The language emphasizes clear intent, low token usage, and no implicit state or side effects.
- Pure functions by default: no implicit state, mutation, or IO.
- Stateful/incremental behavior: anything that must remember across refreshes runs must live in extras (or explicit state passed back in).
- Intent-first pipelines:
x |> f(a)meansf(x, a)(input becomes the first argument). - Pipeline style rule: use
|>only to flatten nested calls; for simple single-call cases, preferf(x)overx |> f. - Externs are explicit: any host-provided function must be declared with
extern fnand anexplain { ... }block. - Parameter order constraint: for each function, the first appearance order of parameters in the body must match the signature order (repeats allowed).
- Match clarity:
matchis a high-level construct with field destructuring and comparisons. - Avoid deep if nesting: when branching gets multi-level, prefer
match(or a helper fn) to keep intent clear. - Guard complexity: when using
for ... if <guard> { ... }, keep guards concise; if the guard is complex, extract it into a helperfn(e.g.,is_valid_row(...)) to keep theforreadable. - Agent rule (important): keep IF Lang as the core logic expression; implement only the minimal required methods in
extra. - Logic placement: all core logic must live in IF Lang; extras provide only the minimal necessary operations.
- IF-first workflow: finish the IF source (data/extern/fn/let plus any demos/tests) before starting on extras, CLIs, or host tooling; only add glue once the IF logic is reviewed.
- Structure: group code by responsibility and mark sections with concise comments.
Top-level items must end with ;:
datatype declarationsextern fndeclarations (must includeexplain { ... })fnfunction definitionsletimmutable bindings
data Tree = Empty | Node { value, left, right };
- Variants are Uppercase.
- Fields are untyped and named.
Node { value: x, left, right }
- Field shorthand allowed:
left==left: left.
fn add(x, y) = x + y;
let n = 10;
- Literals:
Int,Bool,String,Bytes - Lists:
[1, 2, 3] - Maps:
#{ 1: 2, 3: 4 } - Unary:
-x,!x - Binary:
+ - * / % == != < <= > >= && || - If:
if cond { expr } else { expr } - Call:
f(x, y) - Pipe:
x |> f(y)(desugars tof(x, y))
match x {
>= 160 => 5;
Node { value, left, right } => value;
_ => 0;
}
Patterns:
_wildcard (if present, must be last)- Comparisons:
< expr,<= expr,> expr,>= expr - Variant patterns with field destructuring:
Node { value, left, right }Node { value: v, left: l, right: _ }
- All functions are pure.
- Externs must be declared and registered at runtime.
- ADT values are represented as variants with field maps.
- Extras can be implemented in Rust (compiled to dylib) or Python (
.py).
- Constructors are parsed only for Uppercase names.
matchscrutinee does not accept constructor literals with braces directly; bind first withletif needed.
examples/bst_topk.if: BST Top-K IF Lang source.examples/bst_topk_extra.rs: Rust extra for externs used by bst_topk.examples/bst_topk_extra.py: Python extra for externs used by bst_topk.
- Provide
if_lang_register(registry)and assignregistry["name"] = func. - Builtins are called as
func(args, ctx)whereargsis a list of values. ctx.call_fn(name, args)can call back into IF functions.