Build from Source

Lambda Prelude is built from OCaml source with dune.

# Requires: OCaml 5.4+, dune 3.0+, menhir 2.1+
git clone <repo-url> lambda_prelude
cd lambda_prelude
dune build

On Windows, the binary is a true native MinGW-w64 executable — no Cygwin DLL at runtime. Source disclosure is under NDA; please get in touch before evaluating for production.

Hello, Prelude

Create hello.lp:

'Hello, Lambda Prelude!' printNl.

Run it under the interpreter:

lambda-prelude run hello.lp

Build it as a native binary:

lambda-prelude build hello.lp --out hello
./hello

The interpreter is for iteration. The AOT binary is the semantic record — when interpreter and AOT disagree, the AOT binary wins.

A Counter Class

Lambda Prelude is functional: values are immutable and classes do not declare instance variables. Each object is an immutable record over fields:.

Object subclass: #Counter fields: (value).

Counter class >> of: n = Counter fields: { value: n }.
Counter >> value = value.
Counter >> inc   = Counter fields: { value: value + 1 }.

c := Counter of: 41.
c inc value printNl.   "=> 42"

inc returns a new Counter. There is no := reassignment in user code — the only binding form is let, written as := at top-level statements.

Branching is Polymorphic Dispatch

There is no if/match at the surface. Branching is done through messages:

(score > 90)
  ifTrue:  ['A' printNl]
  ifFalse: ['B' printNl].

name ifPresent: [:n | n printNl]
     ifAbsent: ['anonymous' printNl].

xs ifEmpty:    ['empty list' printNl]
   ifNotEmpty: [:h :t | h printNl].

This is the same convention used by traditional Smalltalk, applied uniformly: every conditional, every option / list destructuring, every retry / timeout branch is a message send.

Protocols Replace Inheritance

Classes do not inherit. Shared behaviour is expressed as Protocols, which act like type classes. A class declares the protocols it satisfies in its protocols: clause.

Protocol subclass: #Comparable
  requires: (#<).

Comparable >> > other  = other < self.
Comparable >> min: o   = (self < o) ifTrue: [self] ifFalse: [o].
Comparable >> max: o   = (self < o) ifTrue: [o]    ifFalse: [self].

Object subclass: #Score
  fields:    (points)
  protocols: (Comparable).

Score class >> of: n   = Score fields: { points: n }.
Score >> points        = points.
Score >> < other       = points < other points.

((Score of: 10) max: (Score of: 3)) printNl.

Score implements <; >, min:, and max: come from Comparable for free.

Two Execution Modes

The same source runs two ways. They share the standard library, the type system, and the actor model.

CapabilityinterpreterAOT
Tree-walk for development iterationyes
Direct OCaml calls for statically resolvable sendsyes
Runtime dispatcher for dynamic sendsyesyes
Multi-domain work-stealing scheduleryesyes
Plugins via Dynlink (PostgreSQL, MariaDB, MySQL, Redis)yesyes

Use run for iteration. Use build for production — the AOT binary lowers statically resolvable sends to direct OCaml calls and routes the rest through runtime method dispatch.

CLI Reference

lambda-prelude run    script.lp                     # tree-walk interpreter
lambda-prelude build  script.lp --out binary        # AOT to native
lambda-prelude test   tests                         # run Test API specs
lambda-prelude new    my-service                    # scaffold a project

Debug tooling:

lambda-prelude run   script.lp --trace-mailbox   # log every mailbox enqueue / receive
lambda-prelude run   script.lp --quiet           # suppress unmatched-message warnings
lambda-prelude run   script.lp --scheduler multi 4  # N-worker domain pool + auto-distribute
lambda-prelude build script.lp --emit            # print the lowered OCaml to stdout
lambda-prelude build script.lp --out script.ml   # write the lowered OCaml to a file

Next Steps