Learn Ball

Everything you need to understand, build, and extend the Ball programming language.

Overview

What is Ball?

Ball is a programming language where every program is a Protocol Buffer message. Instead of text files that are parsed into ASTs, your code starts as structured data β€” a protobuf message that can be serialized, stored in databases, sent over gRPC, inspected, transformed, and compiled to any target language.

The language schema is defined in a single file: proto/ball/v1/ball.proto. This proto file is the canonical source of truth for what a valid Ball program looks like.

How to read this page: the Getting Started section is a hands-on how-to (install and run Ball). The Program Structure, Expression Tree, and Standard Library sections are reference material. The Design Principles section explains the why behind the design. Each is self-contained β€” jump to the one that matches what you need.

Core Design

Design principles

1. One input, one output per function

Every function takes a single input message and returns a single output message, following the gRPC pattern. This is not a limitation β€” it IS the design. Multiple parameters are expressed as fields of the input message.

2. Code is protobuf messages

The entire AST β€” expression tree, modules, functions, types β€” is defined in ball.proto. Programs can be serialized to binary protobuf or JSON. If it deserializes, it’s structurally valid.

3. Semantic vs. cosmetic boundary

The expression tree, function signatures, type descriptors, and module structure are semantic. Everything else (visibility, mutability, annotations, syntax sugar) is cosmetic metadata. A Ball program with all metadata stripped computes the same result.

4. Base functions have no body

Base functions are declared with isBase: true and have no body expression. Their implementation is provided by each target language’s compiler/engine. This is the extensibility mechanism.

5. Control flow is function calls

if, for, while, switch, try β€” they’re all std base functions. This keeps the language uniform. Compilers must handle them with lazy evaluation (don’t evaluate all branches before choosing one).

6. Types use protobuf descriptors

Ball does NOT invent its own type system. It uses google.protobuf.DescriptorProto and FieldDescriptorProto, which already define how types map to every target language’s native types.

Program Structure

Anatomy of a Ball program

A Ball program is a Program protobuf message that contains modules, which contain functions, types, constants, and imports.

Program structure (pseudo)protobuf
Program
 β”œβ”€β”€ name, version, entryModule, entryFunction
 └── modules[]
      β”œβ”€β”€ name, description
      β”œβ”€β”€ typeDefs[]        (TypeDefinition)
      β”œβ”€β”€ typeAliases[]     (TypeAlias)
      β”œβ”€β”€ enums[]           (google.protobuf.EnumDescriptorProto)
      β”œβ”€β”€ moduleConstants[] (Constant)
      β”œβ”€β”€ functions[]       (FunctionDefinition)
      β”œβ”€β”€ assets[]          (ModuleAsset)
      └── moduleImports[]   (ModuleImport)
Expression Tree

The seven expression types

Every Ball computation is one of seven expression types. This is the core of the language:

ExpressionMeaningExample
call Function call {module, function, input}
literal Constant value int, double, string, bool, bytes
reference Variable reference "input" = function parameter
fieldAccessField access{object, field}
messageCreation Construct message {typeName, fields[]}
block Statement block let-bindings + result expression
lambdaAnonymous functionClosures

Blocks contain Statements (either LetBinding or bare Expression) followed by a result expression. LetBindings bind a name to a value with optional metadata (type, mutability: var/final/const).

Standard Library

Built-in modules

Ball comes with a comprehensive standard library. All standard library functions are base functions β€” their implementation is provided by each target language.

ModuleFunctionsDescription
std 100+ Arithmetic, comparison, logic, bitwise, string, math, control flow, type ops, cascade, spread, invoke
std_collections50+List, Map, and Set operations
std_io10+Console, process, time, random, environment
std_memory35+Linear memory (C/C++ interop)
std_convertβ€”Encoding/decoding and value conversions
std_timeβ€”Dates, durations, and clocks
std_fsβ€”Filesystem access
std_concurrencyβ€”Futures, isolates, and async coordination

std module highlights

Arithmetic add, subtract, multiply, divide, divide_double, modulo, negate
Comparison equals, not_equals, less_than, greater_than, lte, gte
Logicaland, or, not
Bitwise bitwise_and, bitwise_or, bitwise_xor, bitwise_not, left_shift, right_shift
Strings string_length, string_contains, string_substring, string_replace, string_split, string_trim, string_to_upper, ...
Math math_abs, math_sqrt, math_pow, math_sin, math_cos, math_pi, math_e, math_clamp, ...
Regex regex_match, regex_find, regex_find_all, regex_replace, regex_replace_all
Control Flow if, for, for_in, while, do_while, switch, try, return, break, continue, throw, rethrow
Type Opsis, is_not, as
Null Safety null_coalesce, null_check
Asyncyield, await
Module System

Dependencies and imports

Ball supports resolving dependencies from four source types:

  • HTTP/HTTPS β€” download from any URL
  • File β€” local filesystem path
  • Inline β€” raw protobuf bytes or JSON embedded in the import
  • Git β€” repository at a specific ref

Each import supports a SHA-256 integrity hash for supply-chain security.

Metadata

Cosmetic metadata system

All metadata is cosmetic β€” it improves round-trip fidelity but doesn’t change computation. Key metadata fields include:

  • FunctionDefinition: kind (function/method/constructor/getter/setter/operator), params, visibility, is_async, is_static, annotations, type_params
  • TypeDefinition: kind (class/struct/interface/mixin/enum/sealed_class), superclass, interfaces, mixins, fields, is_abstract
  • LetBinding: type, mutability, is_final, is_const, is_late
  • Module: language-specific imports (dart_imports, cpp_includes, python_imports, go_imports, etc.)
NPM Packages

TypeScript / Node.js ecosystem

Ball ships four npm packages under the @ball-lang scope:

@ball-lang/engine β€” Tree-walking Ball interpreter for Node.js
@ball-lang/compiler β€” Ball β†’ TypeScript compiler via ts-morph
@ball-lang/encoder β€” TypeScript β†’ Ball encoder using the TS compiler API
@ball-lang/cli β€” CLI tool for running, compiling, and encoding Ball programs
# Install the engine
npm install @ball-lang/engine

# Run a Ball program
npx @ball-lang/cli run program.ball.json

# Compile Ball β†’ TypeScript
npx @ball-lang/compiler program.ball.json -o output.ts
Self-Hosting

Ball compiles its own interpreter

Ball achieves self-hosting: the Dart engine is encoded to Ball IR, then compiled to both TypeScript and C++. The compiled engines execute the full conformance corpus with byte-identical output to the reference Dart engine. Exact pass tallies are tracked by CI β€” see the conformance matrix in .github/workflows/.

Dart β†’ Ball β†’ TypeScript Conformance corpus passes, runs on Node.js
Dart β†’ Ball β†’ C++ Conformance corpus passes, compiles via MSVC/GCC/Clang
Scale Validation Round-trips real-world pub.dev packages
Getting Started

Quick start guide

Node.js / TypeScript

# Install and run
npm install @ball-lang/engine

# In your code:
import { BallEngine } from "@ball-lang/engine";
const engine = new BallEngine(programJson);
// run() is async and resolves to the captured stdout lines
const output = await engine.run();
console.log(output.join("\n"));

Dart

# Clone Ball
git clone https://github.com/Ball-Lang/ball.git
cd ball

# Install Dart dependencies (pub workspace at the repo root)
dart pub get

# Run the engine tests
cd dart/engine && dart test

# Compile an example to Dart with the unified CLI
cd ../cli
dart run bin/ball.dart compile \
  ../../examples/hello_world/hello_world.ball.json

C++

# Build the C++ implementation
cd cpp && mkdir -p build && cd build
cmake .. && cmake --build .

# Run the self-host conformance suite (ctest, label "selfhost")
# (already in cpp/build from the step above)
ctest -L selfhost

The proto schema is published on Buf: buf.build/ball-lang/ball