What Can MELP Do?

From language design to binary safety β€” the core features.

Every claim on this page carries an evidence stamp: βœ… working  Β·  πŸ”¬ partial / infrastructure exists  Β·  ⏸️ design intent, not a measured fact. A claim without an honesty line is not published β€” that is a house rule, not a disclaimer.

🧭 1. Scope-Oriented Programming β€” The Model πŸ”¬ Partial

Scope is the universal lifetime unit.

MELP's fundamental abstraction is the scope β€” the way the object is in OOP, the function in FP, the actor in the Actor Model. But the claim is not "we use scopes". The claim is that scope is the single unifying lifetime abstraction: MELP proposes a unified Scope-Oriented Programming model where scope is the primary lifetime abstraction governing memory, resources, concurrency, state, and UI.

#Domain scope governsRule
1Memoryscope closes β†’ memory is returned
2Statestate has no separate life; it lives in a scope
3Concurrencyseparate scope β†’ no shared state β†’ no race can form
4Resources / teardown orderdestruction is the reverse of construction; no manual cleanup
5Interface treethe widget tree is the scope tree; a button is the visible face of a scope
6Freeze / suspensiona branch freezes, its state is preserved, its siblings keep flowing

Layering β€” scope β‰  custody β‰  destroy. Scope is the high-level organising principle; custody is one particular resource-lifetime model running on top of it; destroy is that model's executor. The three are not the same thing. This narrows the claim and strengthens it at once: we do not say "custody is the best memory model" β€” custody is the first proven realisation, not the only possible one. If custody failed, the conclusion would be "this realisation is wrong", not "Scope-Oriented Programming is wrong".

Honesty line β€” the relatives. The pieces of this idea exist in the literature. None of them is new on its own; what distinguishes MELP is the combination. RAII (C++) binds cleanup to the type and covers resources only; region-based memory (Tofte–Talpin) covers memory only; ownership/borrowing (Rust, Cyclone) covers memory and resources; structured concurrency (coroutineScope, StructuredTaskScope, Trio nurseries) covers task lifetime only; component trees (React, Flutter) cover UI and state, and as a framework rule rather than a language one; process isolation (Erlang/BEAM) covers memory, concurrency and failure. MELP's scope aims to cover all six.

Erlang note: we are not alone in having a single unit β€” Erlang's unit is single too (the process). The difference is that Erlang's isolation is harder (separate memories, the wall cannot be pierced), while MELP's wall is drawn by the compiler and the guarantee ends the moment you write external. In exchange, you cannot build a GUI tree or memory custody out of a process. Different trade-offs β€” neither is superior.

Evidence status: of the six domains, two are measured β€” memory (4/4 verified deaths, zero leaks, from the runtime registry) and concurrency's "no data race can be constructed" (four adversarial programs, structural refusal). State, interface and teardown order are architecturally resolved but have no end-to-end evidence board yet.

On novelty: lexical scope is ubiquitous in computer science and every ingredient above has precedent. What MELP proposes is the synthesis β€” scope as the single lifetime abstraction across all six domains at once β€” and we have not found that combination named or defined elsewhere. We stop short of "the first", though: a formal literature survey has not been done, and until it has, that word appears nowhere on this site. The claim is not smaller for it β€” only the sentence is.

πŸ›‘οΈ 2. Memory Safe β€” No GC, No Annotations βœ… Working

In MELP every value belongs to the scope it was born in, and is released when that scope closes (custody). Cleaning up memory is the scope's job, not the programmer's β€” neither a runtime garbage collector nor a manual annotation system like Rust's borrow checker is required.

Language Speed Memory Safety Extra burden
C / C++ βœ… ❌ Manual β€”
Java / Python ❌ GC βœ… Garbage collector
Rust βœ… βœ… Borrow checker annotations
MELP βœ… βœ… None β€” the scope boundary is enough

Lifetime = scope: a variable's lifetime is the lifetime of the scope it sits in β€” there is no independent "state" layer that must be managed separately. When you need longer-lived data you declare it in a higher scope; the rule does not change, only the owner does.

Honesty line β€” four open gaps, stated plainly.

(1) Scope of custody. Custody today covers big numbers and printed strings β€” that front is valgrind-proven (zero leaks). General string temporaries are not yet under custody and do leak. This is a known, addressed limit that the unified-custody work will close. "The programmer does not manage memory" is true today within that scope.
(2) Bounds checking. The architecture of the answer is drawn but the optional sentinel-collection layer is on the shelf; today bounds are handled through the existing list path.
(3) Overflow. In the default world, i64 overflow wraps silently β€” a deliberate cost, and one we are now stating rather than leaving unannounced. Checked-mode intrinsics exist as infrastructure.
(4) The FFI boundary. The moment you write external, the guarantee ends β€” MELP's equivalent of Rust's unsafe. The trust base under all of this is a young C runtime shim; a permanent valgrind suite is the backbone of the safety claim and is still an outstanding debt.

⚑ 3. LLVM Native Binary βœ… Working

MELP is not a syntax experiment. Source is compiled to LLVM IR, and then through llc and the platform linker into a real ELF binary. No GC, no interpreter, no overhead.

Terminal
# Inspect the LLVM IR
bin/run_melp example.mlp --ir

# Produce a native binary
melp_compiler example.mlp -o a.out

# Run it β€” no lli, no interpreter
./a.out

The resulting binary is executed directly by the operating system, without lli or any runtime. LLVM optimizer passes can be applied to reach performance comparable with C/C++.

πŸ”€ 4. Concurrency β€” A Data Race Cannot Be Constructed βœ… / ⏸️

MELP's answer to concurrency is not a primitive you reach for; it is the absence of the condition that makes races possible. The scope boundary is the isolation boundary: a child cannot write to its parent's variables, so two scopes have nothing to contend over. Sharing goes through three controlled gates only β€” tunnel (temporary custody), peek (read-only snapshot) and channel (a copy of a message). Because mutation happens at a single point, the language has no mutex and no atomic keyword β€” there is nothing for them to guard.

βœ… Measured: four deliberately adversarial programs β€” each one an honest attempt to construct a data race β€” were rejected structurally rather than caught at runtime. The failure is at the language level, not a diagnostic bolted on top.

⏸️ Honesty line β€” what we do NOT claim. "Isolation is a licence for free parallelism" is a design intent, not a measured fact. There is no legitimate gate implemented yet, and the runtime is still single-flow: today MELP does not execute your scopes in parallel. We are also not shipping async, await, spawn, threads, coroutines, futures or callbacks β€” these are deliberately rejected imported patterns, not features awaiting implementation. Erlang has known the right answer for thirty years while the mainstream still locks mutexes; our route is scope + channel + event loop.

πŸ” 5. There Is No State Management β€” Because State Has No Separate Life πŸ”¬ Partial

Flutter manages state; MELP keeps it alive.

The root of the Flutter/React pain is this: the interface tree dies and is reborn on every build, while the data must persist β€” two lifetimes at war with each other. StatefulWidget, setState, Provider, hooks... every one of them is a layer for managing that mismatch.

MELP does not manage the mismatch, it removes it: the widget tree is the scope tree, and scope is lifetime. Data lives as long as the scope it was born in. The question "where do I put this state, should I lift it up?" cannot arise by definition β€” because in MELP there is no concept of "state" with a lifecycle of its own.

Honesty line: variables obviously exist β€” the claim is not "there is no data", it is "there is no separately-lived state concept and no layer managing it". The closest relatives are Phoenix LiveView (process = state owner) and Svelte (the spirit of compiling the reactivity layer away). Our difference: unifying the lifetimes is a language rule, not a framework trick, and it extends to the lifecycle through freeze. πŸ”¬ It works in the desktop Melpion; tunnel/channel are partial and it has not yet been proven in a large application.

🧊 6. Freeze β€” Suspend, Don't Kill πŸ”¬ Partial

Life has three verbs: live, sleep, die.

If everything is a scope, and a scope is a unit of life, one consequence is unavoidable: any running branch can be frozen from the outside together with its subtree. State is preserved exactly β€” counters, timers, everything in memory. It then resumes where it left off, or is closed with cleanup. Freezing is selective: sibling scopes keep flowing.

The most concrete payoff shows up in security incidents. When you detect an attack on a server, the classic choice is painful: you kill the process, the attack stops, and the volatile evidence β€” session keys, injected code, connection state β€” evaporates with it. In MELP you freeze that subsystem instead:

ResultWhy
The attack cannot advance A frozen scope cannot take a single step
Evidence is preserved The process never died, so memory stands as it was
The evidence perimeter is exact A scope owns all of its memory (custody) β€” its boundaries are known
Service continues Other regions / sibling scopes keep flowing

Working today: the "selective freeze" scenario on the Live Demo page shows this in the browser β€” three scopes run independently, one is frozen, its siblings keep flowing, and when it is woken it continues from the tick it stopped at. The tick values come from the WASM runtime; nothing is stored in the interface. See it in the Live Demo β†’

Honesty line: the right word is not "firewall" but quarantine β€” freeze does not filter traffic, it halts the subsystem entirely. This is an application-level control, not an operating-system-level security boundary; victim data in RAM stays in RAM, so operational procedure is still required. Kotlin's suspend is function-level and cooperative from the inside; Erlang's sys:suspend is process-level (the closest relative); OS SIGSTOP and VM pause are coarse-grained and language-blind. πŸ”¬ Today it works through an external host API; a scope freezing itself from within the program, and full integration into the language syntax, are still in development.

πŸ”’ 7. The Type System β€” Three Types, Zero Type Anxiety βœ… / πŸ”¬

MELP has exactly three variable types:

TypeDefault representationRange
numericraw i64 β€” one CPU instructionup to 18 digits; with import bigdecimal, unbounded + decimals
stringhandle β€” inline or heaptext of any length
booleanβ€”true / false

In classic languages the programmer agonises over "should this be int8 or int64, float or double?" In MELP that anxiety does not exist. You declare the type you need, and the type name is the declaration β€” there is no dim/var/let ceremony, and typing stays fully static.

MELP β€” declarations
numeric small   = 1
numeric big     = 9999999999999
string  message = "Hello"
boolean active  = true

-- The decimal mark is a comma, which is why
-- ';' separates arguments.
import bigdecimal
numeric pi      = 3,14159265358979323846
numeric amount  = 19,99

The two-world model: numeric is raw i64 by default β€” a single CPU instruction, zero overhead, proven at the IR level. When a file says import bigdecimal, that file gains automatic big-number promotion driven by the hardware overflow flag, plus 3,14 decimal support stored as mantissa + scale (not floating point), so 0,1 + 0,2 is exactly 0,3. The principle: nobody pays for what they are not going to use.

Honesty line: the default world (i64) is proven βœ… and free. The import bigdecimal world is πŸ”¬ β€” the mechanism works, but until the test corpus covers it, it is not recommended for production. Python carries every number on the heap and therefore always pays; Rust and C wrap or are undefined on overflow and therefore never pay, accepting the risk. "Free by default, automatic correctness on request" is the distinctive position. Note the cost we accept: in the default world, i64 overflow wraps silently. Go and Rust infer types too, but with a rich type vocabulary; MELP's claim is plainness β€” three types, zero ceremony.

The decimal comma is a feature, not a locale. 3,14 β€” the decimal separator used by most of the world's population is absent from every mainstream language. It is present in MELP, and it is why ; is the argument separator. Using it without the import is caught by an explicit compile error rather than a silent misreading. We know of no direct equivalent (spreadsheet formulas do this by locale β€” that is not a language).

βœ”οΈ 8. OK Is a Value β€” Errors Resolve Inside the Scope βœ… / πŸ”¬

There is no void type in MELP. Functions that "return nothing" actually return OK β€” a real value meaning success, carried as an i64 in the ABI. Mapping OK onto LLVM's void is a codegen error, and once was a real one: it was the root of an exit-code corruption bug.

Errors are handled with the same logic: an error is resolved inside the scope where it was born and does not spill outward. If it must travel, you mark that explicitly. There is no invisible exception chain; error propagation is readable in the code itself.

Honesty line β€” including a correction. This page previously said that no return 0 ceremony is written at the end of main. That was wrong about today's compiler. The canonical entry point is numeric function main() with return 0 β€” that is what compiles, and it is what the compiler's own source uses. OK function main() was a design intent that never reached the code. The "no void, OK is a value" philosophy holds for every other function; main is the exception.

The error-handling structure in use today is the expect block (parser and codegen βœ…). The ? propagation operator β€” a relative of Rust's, differing in that OK is a value rather than a type and that errors are bound to the scope lifecycle β€” and the Result type are still experimental πŸ”¬.

β˜• 9. There Is No Stack and No Queue β€” Because They Are in the Fabric πŸ”¬ Partial

MELP has no built-in Stack or Queue data structure β€” and needs none, because both are already woven into the language.

LIFO is the life order of scopes. Scopes close in the reverse of the order they opened; destruction is the reverse of construction. The programmer does not manage a stack β€” they open and close scopes.

FIFO is the delivery order of a channel β€” with its slogan: the coffee will not go cold. The queue of the scope that finished its work drains first; the ready order is served while it is hot, and no message sits on the counter while later jobs run.

Honesty line: every language rests on a call stack (LIFO is always there underneath) and Go's channels are FIFO too. MELP's claim is to be the single model that removes the need to present these additionally as a data structure or a concurrency principle. πŸ”¬ The channel/tunnel implementation is partial; an algorithmic stack or queue is built by hand on top of a list today β€” a known gap, noted here rather than hidden, and not part of the claim.

πŸ› 10. The debug Block β€” A Letter From the Past to the Future βœ… Working

A comment may go unread. A letter runs.

The quietest loss of institutional memory is this: the code stays, the intent dies. The architect who wrote the system retires; nobody reads the comments they left behind. Worse, those comments eventually become lies β€” "the member count will never pass ten thousand" was written, and was true, and then the world moved and the program silently became wrong.

In MELP you turn the assumption into a sentry that runs:

MELP β€” a debug block
debug
    if member_count > 10000 then stop
    -- Past ten thousand members this design may need
    -- a new database architecture. β€” 2026, the first team
end debug

Ten years later, long after that architect has left, on the day the member count passes ten thousand, the program stops and makes someone read the letter. Assumptions never quietly go stale.

Zero cost in production: compiled with MELP_RELEASE=1, debug blocks are stripped entirely β€” not a trace remains in the production binary. Today you can already break on every block in a debugger (gdb/lldb).

Honesty line: the closest relatives are JavaScript's debugger; statement, C's assert and Eiffel's design by contract. MELP's version is distinguished by combining block form + condition + explanation: assert crashes on violation (taking the evidence and the service with it), while MELP's stop aims to freeze the violated scope β€” state preserved, siblings still flowing, letter read. Violation becomes quarantine plus briefing rather than catastrophe. That freeze integration is still at design stage.

One open language decision, stated openly: stripping in release is in tension with the very scenarios that make this feature most valuable β€” a letter should not be deleted exactly where it is needed. The design is therefore heading towards levelled blocks: debug stripped as today, alongside a debug keep that survives into production so a critical sentry can still run in the environments that need it most β€” at a cost of one comparison. Making release-strip opt-in is the alternative under consideration. The choice is scheduled for the first language session after the peripherals phase; we are naming the candidates rather than the verdict because the verdict is not measured yet.

πŸ’€ 11. The Dead Module Philosophy βœ… Working

A natural consequence of the scope model: modules are dead by default. No module allocates resources or occupies memory on its own. The scope that uses a module brings it to life; when that scope closes, the module dies with it β€” zero idle resources, no garbage collector needed.

Consequence: memory cleanup is the scope's job, not the programmer's β€” without a GC and without a borrow checker. Every value belongs to the scope it was born in (custody); when the scope closes they are released together, and the custody of a value returned via return is transferred to the caller.

Honesty line: custody today covers big numbers and printed strings β€” that front is valgrind-proven (zero leaks). General string temporaries are not yet under custody and do leak; a known, addressed limit that the unified-custody work will close.

πŸ“Œ 12. Named Scopes β€” scope βœ… Working

scope name gives you a clean conditional exit out of nested blocks. No flag variables and no exceptions are needed in deep loops. Every structure opens with an ID card and closes with a signature β€” the closing name echoes the opening one, the way an HTML tag does.

MELP β€” scope
scope search
    loop each i in rows
        loop each j in columns
            if found(i; j) then
                exit search  -- clean exit from nested loops
            end if
        end loop
    end loop
end scope

Note: exit leaves a scope; return terminates the program with an exit code. There is one loop construct β€” loop β€” with no while and no for. The scope keyword itself is optional rather than mandatory: a verifiable declaration of intent.

🎨 13. The Interface Is a Drawing β€” GUI = Scope Tree πŸ”¬ Partial

In most languages the interface is written in a separate language: XML, QML, HTML, JSX. In MELP the interface is defined with the language's own scopes β€” because the two are structurally the same thing. The mapping between <a><b/></a> and scope a … end scope is one to one: element = scope, nesting = ownership, closing tag = name echo, id = scope name, attribute = property.

The practical consequence: you design your interface by drawing it. You draw in a design tool (Figma or similar) and export SVG; layer names correspond to scope names, and you write the behaviour in MELP. Because the widget tree is the scope tree, the rule "a child dies with its parent" applies in the interface exactly as it does in memory.

"SVG gives the tree a shape; scope gives the tree a life β€” in HTML the tags are dead, in MELP they live, sleep and die."

Where it is today: an SVG designer and the .svge format work inside the MELP editor extension β€” it recognises widgets (label, textbox, combobox, checkbox, button) and generates the skeleton of the MELP event handlers bound to them.

Honesty line: what works is the designer and .svge β†’ MELP skeleton generation. The "one click from drawing to running application" flow does not exist yet β€” today you write the body of the generated skeleton yourself. SVG projection in the browser and full embedding of the GUI into the language syntax are in development. The closest relatives are QML, SwiftUI and Flutter; our difference is that the interface is not a separate DSL but a natural extension of the language's memory and lifetime model.

🧱 14. Struct + Function β€” No Classes βœ… Working

In MELP data is defined with struct and behaviour with free functions. The same modelling power without an OOP hierarchy, at far lower complexity. A struct is a dead template: new means "revive the dead template", and it is the only place new is used β€” a scope is already alive and runs by being called.

MELP β€” struct + enum + match
struct Point
    numeric x
    numeric y
end struct

Point p = Point(10; 20)
Point q = new struct Point(1; 2)

enum Colour
    Red
    Green
    Blue
end enum

Colour c = Colour.Blue

match c
    case Colour.Red then println("red")
    case Colour.Blue then println("blue")
end match

πŸ—‚οΈ 15. Modular Architecture βœ… Working

Every module has a single responsibility; a central orchestrator is forbidden. Build order is derived automatically from the dependency graph, and circular dependencies are reported as compile-time errors.

Design principle: modules cannot write to each other's variables β€” the scope boundary is the isolation boundary. Sharing passes through three controlled gates only: tunnel (temporary custody), peek (read-only copy), channel (a copy of a message).

Honesty line β€” supply chain, in three classes. This section is about how the compiler's own source is organised, not about dependency management. Supply-chain risk splits into three, and MELP sits differently in each β€” collapsing them into one verdict would be dishonest in either direction.

βœ… Package-ecosystem class (left-pad, log4shell, typosquatting): structurally absent. MELP has no third-party package ecosystem, so transitive dependencies β€” and the risk that rides on them β€” cannot form.
❌ Build-chain class (xz): fully exposed, no answer. Every MELP binary rests on LLVM/clang, libgmp, libc and the linker. The xz backdoor was precisely this class β€” a compromise of the build process, not of a package. We have no mitigation for it today.
πŸ”¬ The seed (Thompson's Trusting Trust): route drawn, not yet walked. The compiler is built from a golden seed. Were that seed ever compromised, the backdoor would reproduce itself and our bit-for-bit N1=N2 gate would still go green β€” the gate proves determinism, not the cleanliness of the seed. Against this MELP has one structural advantage: the IR it emits is text, so a seed-injected backdoor must appear there, an inspection point most self-hosting languages do not offer. The seed shelf and N-1 chain are the raw material for diverse double-compiling. Neither is exercised yet.

Closed source does not fix this. MELP is closed source, and it would be easy β€” and wrong β€” to present that as a supply-chain answer. It is not: it removes independent audit, reproducible-build verification and the user's own ability to check what they run, so it lowers verifiability rather than raising it. The distinction that matters: the trust a developer holds and the trust a user can independently verify are not the same thing.

🌐 16. Platform Independence πŸ”¬ Partial

Write once, run anywhere. Thanks to the LLVM backend the same source produces native binaries for Linux, macOS and Windows, with no separate cross-compilation tooling.

Current status: Linux x86-64 is fully supported. macOS and Windows support is expected with the beta release.

🎯 17. Self-Hosting β€” The Compiler Compiles Itself βœ… Working

The MELP compiler is written in MELP and compiles its own source. Self-hosting is a maturity threshold for a language, and in MELP it works today β€” the C++ bootstrap era is behind us.

More than that, it is a mandatory gate on every change: the compiler compiles its own source (N1), the resulting compiler recompiles the same source (N2), and the two outputs must be bit-for-bit identical. If they differ, the change is reverted. The compiler proves itself again every day.

The evidence chain: golden β†’ N1 β†’ N2 β†’ N3, with diff = 0 at every step, followed by compiling and running an independent user program. If that chain is not green, no change is accepted.

Honesty line: a bootstrap test exists in every self-hosting language. MELP's difference is that it is a mandatory gate on every change, with no exceptions. This is our answer to build rot; the answer to assumption rot is the debug block above.

πŸ“œ Origins β€” Where the Name Came From Historical β€” not part of the current claim

MELP began life as MLP Β· Multi Language Programming. The original idea was that program logic should be independent of the human language and the syntax style it is written in: one compiler reading Turkish, English, Russian or Japanese source, with a new language added by writing a keywords.json file rather than touching the compiler; and alongside that, a choice of three syntax styles β€” natural MELP, C-like braces, Python-like colons β€” normalised into a single internal representation before the compiler proper ever saw the code.

That layer is no longer part of the language claim. The name stayed; the focus moved to scope. What MELP proposes today is stated in Β§1 above: scope as the primary lifetime abstraction. We record the origin here because it is true and because it explains the name β€” not as a feature on offer.

Why it was set down: a language earns attention with one idea, not with a list. Multi-language keywords were a normaliser feature β€” real, but peripheral to what makes MELP worth using, and it competed for the front page with the thing that actually distinguishes the language. The honest position is the one on this page: one claim, at full size, with its evidence marked.

Try It

You can test all of this from the browser, with nothing to install.