LPScript

Functional Smalltalk with actors, for screens that run in a browser

An actor never pulls from its mailbox

Its methods are its message handlers, and a handler answers the next state. Nothing captures a continuation, so nothing needs fibers, generator trampolines or effect handlers. Where a handler has to wait, JavaScript's own await is the continuation it already has.

You never say what to redraw

There is nowhere to declare it. A handler answers the next state, so whether it moved is a comparison. No memo, no dependency arrays, no subscriptions — not one line is written for the sake of redraw performance.

A written decimal is exact

1234.50 is a Decimal, not a binary float, because a language for business systems cannot get money wrong by default. Division is exact or it refuses, and rounding is spelled in business terms with its direction.

The designer's HTML is the markup

Rows and dialogs are the designer's own samples, cloned. There is no second copy of that markup written in another language, and a selector that the page does not have is a compile error.

What you write with it

Screens that run in a browser: lists, searches, detail panes, editing and saving, values handed from one page to the next, HTTP and WebSocket — and screens that redraw on every frame.

The mechanism is the same for all of them. State lives in an actor, what arrives is a message, and what draws is the state having moved. A button press, an HTTP reply and an animation frame are written the same way.

A ten-thousand-row grid, a breakout and a shooter on a canvas are all built from that and nothing else. Nothing was added to the language for the games.

The defaults are right for business data. Money is a Decimal, a date is a calendar day, JSON keeps its digits. Those are the screens that come up most often — not the only ones you can write.

Actor subclass: #Counter fields: (count).

Counter class >> init: n = Counter fields: { count: n }.

Counter >> inc = self with: { count: count + 1 }.
Counter >> get = self reply: count.

c := Actor spawn: (Counter init: 0).
c tell inc.
(c ask get) forced printNl.          "1"

This is a sibling of Lambda Prelude, not a backend of it. Lambda Prelude is a native, OCaml 5 language for long-running backend services. LPScript keeps its shape — immutable values, protocol composition instead of inheritance, Maybe instead of nil — and nothing of its runtime.

What stops before it runs

A message no handler answers never reaches the runtime: c tell increment is a compile error when Counter has no such handler, and ask is typed by what that handler replies.

An actor may become: a different class — that is what replaces selective receive — and every phase still has to answer what references to it were typed at. That is checked at compile time too.

A block cannot cross an actor boundary, because it closes over the scope it was made in. That was settled before there was a boundary to cross, so the day workers arrived there was nothing to go back and fix.

One reading each

Money, dates and JSON have one reading each. Date is a calendar day with no time and no zone. JSON carries its numbers as the text they arrived as, so 1234567890123456789.12 is still that number after a round trip.

Where it runs

The browser, on ordinary ES modules. A screen is one .lps file plus the components it imports, and a build is per screen. Checking a 223-line order screen reported 0.000 seconds: faster than the clock ticks.

Heavy work moves to a Web Worker by writing Worker spawn:on:. The reference does not say where the actor runs.

License and Contact

LPScript is proprietary software, all rights reserved. Copying, modification, or redistribution of any part of LPScript requires the prior written permission of Lambda LLC. It is not open source.

Binaries (the lpsc compiler, the language server, the runtime, and the editor extension) are provided free of charge, strictly as-is, with no warranty and no liability of any kind. You may evaluate and use the binaries at your own risk; we do not guarantee fitness for any purpose, do not provide support unless separately contracted, and accept no responsibility for damages arising from their use.

Source code is disclosed only to partner companies that have signed an NDA — the NDA and the accompanying agreement constitute the "prior written permission" required by the license. Source access is bundled with a paid engagement (joint development, customization, integration); it is not sold as a standalone product.

If you are considering LPScript for building business screens or for production use, please get in touch. We usually work together in one of three ways.

Contact: Lambda LLC