SLIP

SLIP is an experimental scripting language designed around a simple question: what would a language look like if execution order stayed visible on the page?

Most mainstream languages ask the reader to perform a translation step. The code is written one way, but precedence rules, special syntax, and hidden evaluation behavior mean it actually runs another way. SLIP was designed to reduce that gap. Its core rule is that expressions are read and evaluated left to right. Instead of depending on precedence to rearrange meaning behind the scenes, the interpreter carries the current value through the rest of the expression, so infix and piped forms follow the same sequence the reader sees.

That idea sits in a lineage. SLIP draws from Forth, Lisp, and especially Rebol.

From Forth it takes the belief that execution order should be simple and central rather than incidental. From Lisp it takes the idea that code should be a real datatype, something the language can pass around, inspect, and execute deliberately. From Rebol it takes perhaps the strongest influence of all: the sense that a scripting language can be flexible, expressive, data-shaped, and practical without becoming syntactically hostile.

The result is not an attempt to imitate any one of those languages directly. It is an attempt to borrow a few of their strongest ideas and combine them into a model that stays compact and readable.

What makes SLIP interesting

Read In Order, Execute In Order

The most visible design choice in SLIP is left-to-right evaluation.

10 + 5 * 2
-- evaluates to 30

10 + (5 * 2)
-- evaluates to 20

In SLIP, there is no hidden precedence hierarchy quietly taking over the expression. The first line is read exactly as it appears: add first, then multiply. If a different order is needed, it is written explicitly with parentheses.

That tradeoff is deliberate. It asks the programmer to give up some familiar precedence conventions, but in return it makes evaluation order local and obvious. A reader does not have to re-parse the line before understanding what it means. The written order and the runtime order are the same.

This also makes the language easier to reason about when expressions become more dynamic. Once dispatch, path lookup, higher-order calls, or runtime-generated code enter the picture, predictable sequencing matters more than mathematical notation habits.

Pipe And Infix Are The Same Underneath

SLIP also treats piping and infix forms as two views of the same underlying mechanism.

mul (add 10 5) 2
-- evaluates to 30

10 |add 5 |mul 2
-- evaluates to 30

The interpreter carries the current value through the expression and feeds it into the next step. That gives SLIP a natural left-to-right style for arithmetic, transformation pipelines, and API-style code without needing separate models for operators versus function calls.

This matters because many languages hide special rules behind ordinary-looking syntax. Experienced programmers stop noticing because they have already learned the exceptions. Beginners have not. SLIP tries to avoid that by building more of the language from a smaller set of ideas. A language gets easier to learn, extend, and explain when the number of underlying mechanisms stays low.

Code Is Data

The second major design choice is that code is a real datatype.

A block of code is not just syntax to be consumed immediately by the parser. It can be passed to functions, stored, expanded, and executed later.

when: fn {condition, then-block} [
    if [run-with condition current-scope] then-block []
]

double: fn {x} [ x * 2 ]
  
value: 10
value: value |add 5 |double
  
when [value > 20] [
    print "value is {{value}}"
]
  
value
-- emits "value is 30" and returns 30

This is one of the places where the Lisp influence shows up, though SLIP arrives there with a very different surface style. In SLIP, code-as-data is not primarily about building a macro system. It is about making the language more uniform. If code can be manipulated as an ordinary value, then many behaviors that would otherwise require dedicated syntax can instead be expressed through the language's ordinary extension mechanisms.

Signatures Are Data Too

SLIP extends that idea to function signatures.

A signature is not just a static annotation attached to a function definition and forgotten. It is itself structured data. That means binding, dispatch, and certain control-flow-oriented patterns can work with signatures directly rather than treating them as opaque metadata.

This is one of the design instincts behind SLIP: if something unusual is happening, I want that to be visible in the code rather than buried in a hidden rule. If a construct has real semantic meaning at runtime, I would rather represent it explicitly in the language model than hide it behind a special rule.

Once code and signatures are both real values, more of the language can happen in the open. You do not need nearly as much magic to make powerful things possible. It also helps keep advanced features conceptually connected. Dispatch, function definition, binding forms, and some control constructs all grow out of the same basic idea: programs should be able to work with structure explicitly.

Extensibility Without A Giant Syntax Surface

One of the original motivations behind SLIP was to make language extension feel less like syntax design and more like programming.

Many languages become expressive by continuously adding dedicated forms: one keyword for this control flow, another for that binding construct, another for a special iteration pattern, another for a conditional variant, and so on. That can work, but it often leads to a language that grows by accumulating exceptions.

SLIP aims for a different balance. It still needs a small bootstrap layer in the host runtime for forms that control evaluation order directly. But above that layer, it becomes possible to define richer control constructs in the language itself.

For example:

loop: fn {body-block} [
    while [true] body-block
]

for: fn {vars, start, end, body-block} [
    var-name: vars.positional[0]
    i: start
    for-scope: current-scope
    for-scope: for-scope["meta"]["parent"]
    step: if [start <= end] [1] [-1]
    while [if [step > 0] [i < end] [i > end]] [
        for-scope[var-name]: i
        run-with body-block for-scope
        i: i + step
    ]
]

That is not merely a convenience. It reflects a deeper design preference: once the runtime can represent code and binding structures directly, many language features no longer need to be built into the parser or evaluator as special syntax.

The benefit is not only flexibility. It is also locality. New abstractions can live close to the domain code that needs them instead of being forced into the language core.

A Language Built From A Small Number Of Ideas

The languages I tend to admire most are the ones that get surprising mileage out of a small number of ideas. They feel expressive because their pieces compose well.

SLIP tries to get leverage from a small set of connected ideas:

  • left-to-right evaluation
  • a threaded current value through expressions
  • first-class code blocks
  • signatures as data
  • paths as a primary access mechanism
  • language extension through functions, code blocks, and signatures
  • multidispatch and resolver-based state ownership

What makes these ideas valuable together is that they reinforce one another. Left-to-right evaluation makes the flow of an expression easier to follow. Code-as-data makes control behavior more programmable. Signature objects make binding and dispatch more explicit. A pipe-first call style makes data transformation read naturally. The language does not need a separate mental model for each of these things if they all rest on the same core machinery.

That compression is one of the main goals in SLIP. A language is not powerful because it has a lot of features. It is powerful when a small set of ideas can do a lot of work.

Rebol's Influence

Rebol deserves explicit mention because it shaped the feel of SLIP at least as much as any formal semantic idea did.

What Rebol demonstrates, and what SLIP takes seriously, is that a language can be highly dynamic and highly expressive without becoming unreadable. It can treat blocks, words, paths, and values as the basic material of programming. It can feel like a language for describing actions on data rather than a language for satisfying a compiler's appetite for punctuation.

That influence shows up in several places:

  • the willingness to make values and code structures first-class
  • the preference for readable, direct scripting over ceremony
  • the idea that a small language can still be highly expressive
  • the belief that practical scripting and language design do not have to be separate concerns

SLIP is not Rebol, and it is not trying to reproduce Rebol exactly. But it shares the conviction that a language can be both flexible and human-readable if the semantic model is chosen carefully.

Why it is approachable

SLIP is also designed to be learnable.

Many languages are taught as if they were simple, but their real behavior depends on a large amount of hidden machinery: precedence tables, syntax exceptions, and special cases that look small at first and add up over time. That creates a gap between what beginners read and what the interpreter actually does.

SLIP tries to reduce that gap. Expressions run in the order they appear. Pipes and infix forms follow the same underlying model. Code blocks are explicit values. New abstractions can often be introduced as ordinary functions instead of as new syntax rules.

That does not make the language shallow. It makes it easier to teach, easier to explore, and easier to trust when you are still learning. In that sense, SLIP is closer in spirit to languages like Logo and Smalltalk: languages that take readability, exploration, and learner access seriously.

The goal is not just to help beginners. It is to keep code understandable under weak context: for new programmers, for developers returning after six months, and for anyone else who has to read the code without guessing about hidden rules and magic functions.

What it is good for

SLIP is designed for systems built from entities, rules, actions, and evolving state. It is especially well suited to world modelling, game logic, workflow-style rule systems, multi-user environments, and AI-assisted interaction.

It is designed first as an embedded scripting language: the layer where people can read, change, and experiment with behavior without first having to understand the whole host application.

Where It Fits Best

SLIP is a strong fit for domains where the core problem is not just computation, but coordination.

  • world modelling
  • game and simulation logic
  • workflow and policy engines
  • multi-user systems
  • embedded domain scripting
  • AI-agent environments

These domains tend to have the same structural pressures:

  • many interacting entities
  • lots of conditional behavior
  • rules that change over time
  • state that should not be mutated arbitrarily
  • a need for scripting without exposing the entire host runtime

SLIP was shaped around those pressures directly.

Rule-Oriented Behavior

Many systems start out simple and then become difficult to maintain because every new case gets added to the same growing conditional tree. SLIP is designed to make rule-oriented behavior easier to express.

Instead of forcing all behavior into a single handler, you can define multiple cases under the same function name and let dispatch choose the right one.

price-for: fn {item: PhysicalItem} [
    item.base-price + item.shipping
]
  
price-for: fn {item: DigitalItem} [
    item.base-price
]

price-for: fn {item} [
    item.base-price
]

This style is useful anywhere behavior differs by type, context, or a small set of distinct cases. Pricing, fulfillment, promotion rules, and access control all benefit from this shape.

State Ownership

SLIP also includes a resolver-based object model for domains where ownership of state matters.

Instead of letting code mutate shared state from anywhere, a resolver owns the state and exposes the operations that are allowed to change it. That makes writes more explicit and easier to reason about.

-- A resolver is an object that owns some state and exposes the operations
-- that are allowed to change it.

Store: resolver #{
    stock: #{ apples: 10 }
    prices: #{ apples: 3 }
}

-- Because the store owns its inventory, purchasing goes through the store's API.
quote-total: fn {this: Store, sku, qty} [
    response ok (this.prices[sku] * qty)
]

purchase: fn {this: Store, sku, qty} [
    available: this.stock[sku]
    if [available < qty] [
        response err "out of stock"
    ]
    total: this |quote-total sku qty
    this.stock[sku]: available - qty
    response ok #{ total: total, remaining: this.stock[sku] }
]

  
Store |purchase "apples" 2
-- returns response ok #{ total: 6, remaining: 8 }

This model works well for:

  • simulation state
  • workflow state
  • business rules with clear authority boundaries
  • shared mutable state in multi-user systems
  • agent-managed environments

Good For Embedding

Many applications need a scripting layer, but do not want to expose the full host runtime or hardcode every rule in the host language. SLIP is intended for that middle layer.

It is useful when the host application needs:

  • a language for describing rules and behaviors
  • a way to evolve domain logic quickly
  • scripts that can be inspected, loaded, and run dynamically
  • a runtime that can model stateful interactions without becoming opaque

That makes it a good fit for systems where the scripting layer is part of the product itself rather than just a developer convenience.

AI And Agent Systems

SLIP is particularly interesting in AI-driven systems because it maps well to domains built from actions, entities, world state, and rules.

An agent environment often needs to express things like:

  • what entities exist
  • what state they own
  • what actions are allowed
  • what happens when those actions run
  • how the world changes in response

Those are natural shapes for SLIP.

It also helps that the language is designed for embedding. A host system can expose a controlled runtime surface to scripts rather than handing unrestricted access to the full application.

Why LLMs Like It

SLIP also works well with LLM-assisted coding.

SLIP was not designed as an AI language. It just happens to share a few properties that help both humans and language models: explicit execution order, regular structure, and fewer hidden rule changes.

  • Explicit execution order: SLIP tends to make execution order visible instead of hiding it behind precedence and special-case syntax. That reduces the amount of mental reconstruction needed from both humans and models.

  • Small semantic surface: Many advanced behaviors in SLIP are built from a relatively small set of ideas: paths, ordinary calls, piped calls, code blocks, signatures, dispatch, and resolvers. There are fewer disconnected mechanisms for a model to memorize.

  • Structured runtime values: Code blocks and signatures are real runtime values rather than purely compiler-side concepts. That makes the language easier to explain, transform, and generate with regular structure.

  • Natural fit for agent-style domains: LLMs are often used in domains involving tasks, actions, plans, entities, and world state. SLIP is already shaped around those same concerns, so the language and the application domain reinforce each other.

Current Status

SLIP is still in the prototyping phase and is under active development.

The current implementation is written in Python, which makes it easier to evolve quickly while the language model, runtime behavior, and embedding story continue to be refined through real use. It is being developed alongside broader work on multi-user worlds, AI agents, and interactive systems.

That means SLIP should be understood as an actively used experimental language rather than a finished platform. The design is real, the implementation is usable, and the project is being shaped through practical application rather than purely theoretical work.

Source

The source code is available on GitHub:

SLIP on GitHub