Slap: A Functional Concatenative Language with a Borrow Checker
#Rust

Slap: A Functional Concatenative Language with a Borrow Checker

Tech Essays Reporter
5 min read

Slap combines the terse expressiveness of stack-based languages with the safety guarantees of linear types, creating a unique programming language that's simultaneously powerful, safe, and small.

Slap emerges as a fascinating chimera in the programming language landscape, blending the terse expressiveness of stack-based languages like APL, J, and K with the safety guarantees of Rust-like linear types, all while maintaining the simplicity of Lisp and Forth. This functional concatenative language with a borrow checker represents an ambitious attempt to create something that is terse, safe, small, fast, and easy to use.

The Essence of Slap

At its core, Slap is a stack language that embraces postfix syntax—often considered ugly but undeniably powerful. The language demonstrates this power through elegant solutions to common programming problems. For instance, generating twenty Fibonacci numbers without recursion becomes a simple matter of: 0 1 20 (swap over plus) repeat drop 6765 eq assert. This terse style extends to more complex operations, with plans to eventually add Uiua-esque glyphs that would make programmers feel like wizards.

For those who prefer more explicit code, Slap offers let bindings as an alternative to tacit stack manipulation. The language supports both styles seamlessly, allowing developers to choose their preferred level of abstraction.

Safety Through Linear Types

The true power of Slap lies in what it cannot do. By implementing parametric types similar to Hindley-Milner and linear types inspired by Rust's borrow checker, Slap prevents a wide range of common programming errors. The type system ensures that mismatched data types cannot be combined—attempting to concatenate a list of integers with a list of floats results in a type error.

Linear types provide particularly strong guarantees around memory management. In Slap, you cannot duplicate or discard a pointer (box) without explicit operations. This prevents classic problems like double-free, use-after-free, and memory leaks. The language enforces a disciplined approach to memory management through specific operations:

  • lend for borrowing read-only snapshots
  • mutate for in-place modifications
  • clone for creating independent copies
  • free for releasing memory

This approach extends beyond just memory management. Linear types prove valuable for file handles and thread coordination, ensuring resources are properly managed throughout their lifecycle.

Flexible Stack Semantics

Slap's stacks are remarkably flexible, capable of serving as tuples, closures, or functions without confusing the type system. This flexibility enables elegant functional programming patterns. For example, tuples can be created and immediately applied: (1 2) apply plus 3 eq assert. Closures can be constructed and used naturally: 'make-adder ('n let (n plus)) def 5 make-adder 3 swap apply 8 eq assert.

The language also supports function composition in a natural way: (1 plus) (2 mul) (3 sub) (sqr) compose 5 swap apply 81 eq assert. This composability, combined with the stack-based approach, creates a powerful foundation for building complex operations from simple primitives.

Type Inference and Stack Effects

While Slap's type-checker automatically infers stack effects, developers can add explicit annotations for clarity and enforcement. This feature is reminiscent of function type declarations in other languages but adapted to the stack-based paradigm. The annotations are expressive enough to describe exotic stack effects, including functions with no effect, multiple return values, and linear parametric effects.

Performance Without Garbage Collection

Slap achieves impressive performance by eliminating garbage collection entirely. Everything sits on the stack unless explicitly allocated on the heap in a box. This approach requires developers to reason about memory tradeoffs explicitly, but it provides predictable performance characteristics. The language demonstrates this capability by solving the first ten Project Euler problems with reasonable execution times, ranging from 3 milliseconds for simpler problems to 7.3 seconds for more complex ones.

Minimal Implementation

The entire Slap implementation consists of approximately 2,000 lines of C99 code, including the lexer, typechecker, and stack evaluator. The author believes this could be reduced by another 500 lines without sacrificing readability or performance, making Slap one of the smallest yet fully-featured programming languages available.

Visual Programming and Effects

Slap extends beyond text-based programming with built-in support for pixel graphics. Developers can build the language with SDL support for native applications or WebAssembly for browser-based execution, resulting in a 640x480 canvas with 2-bit grayscale. This lo-fi aesthetic, inspired by Uxn, enables creative visual programming.

The language also provides type-safe, memory-safe managed effects for interacting with the host system. This includes event handling for ticks, keydown events, and mouse interactions, all integrated seamlessly with the type system.

Real-World Examples

Slap demonstrates its capabilities through practical examples like Conway's Game of Life and a simple dots game. The Game of Life implementation showcases the language's ability to handle complex state transitions and neighbor calculations efficiently. The dots game demonstrates interactive graphics and simple physics simulations, all expressed in Slap's concise syntax.

The Future of Slap

Slap represents an intriguing experiment in programming language design, successfully combining multiple paradigms that rarely appear together. Its combination of stack-based terseness, linear type safety, and minimal implementation creates a unique tool for both learning and practical application. While it may not replace mainstream languages for all use cases, Slap offers valuable insights into how different programming paradigms can be combined to create safer, more expressive languages.

The language's emphasis on what it cannot do—preventing common errors through its type system—represents a refreshing approach to language design. By making certain operations impossible rather than just discouraged, Slap guides developers toward safer patterns while maintaining the expressive power needed for complex programming tasks.

For developers interested in exploring alternative programming paradigms, Slap offers a compelling playground that combines the best aspects of functional programming, stack-based languages, and modern type systems. Its small implementation makes it accessible for study and modification, while its powerful features make it capable of solving real programming problems efficiently and safely.

Comments

Loading comments...