Explore Panackelty

Small language.
Serious capabilities.

Exact arithmetic. Types that express your rules. Compiler-checked effects. Generic functions, exhaustive pattern matching and async/await.

Panackelty brings these together in a language for building terminal tools, processing data and exploring algorithms. Its compiler is written in Panackelty itself—and the toolchain can explain selected parts of its reasoning.

Available today: native developer preview 0.1.0-alpha.11 for Linux x86-64 and Apple silicon. The browser playground runs a separately pinned version with different host capabilities.

Native alpha.11 · Browser v0.1.1

Native alpha.11 · experimental

Language features

Exact answers, without hidden rounding

A third should stay a third until your program decides otherwise.

Panackelty supports arbitrary-precision whole numbers, exact base-10 decimals and exact fractions. Calculations preserve their meaning without silently substituting floating-point approximations.


main(): Void {
  third = 1/3
  print((third * 30).nat())  // 10
  print((1/8).dec())        // 0.125
}

Choose Nat, Int, Dec or Rat to describe the values you need. Conversions are explicit: an exact conversion that cannot represent its input fails rather than quietly rounding.

Use exact values for totals, proportions, unit calculations and algorithms where a small approximation can change the answer.

Types that express your rules

“An integer” is often too broad. Your program may need a positive quantity, a bounded index or a valid port number.

Guarded types put those constraints into the type itself:


type Positive = Int where value > 0
type Port = Nat where value >= 1 && value <= 65535

The compiler checks that values satisfy supported constraints before accepting their use. It can reason from literals and facts established by surrounding conditions.

That moves some checks out of comments and into the language.

The current proof system is deliberately bounded. It does not prove arbitrary properties or silently add runtime validation for untrusted input.

Data models with every case accounted for

Records give related values named fields. Tagged unions describe alternatives explicitly. Exhaustive pattern matching makes you account for every variant.

Represent a result, a state or a command as the data it actually is, then handle its possibilities with match.

When a type describes several cases, the compiler checks that your match covers them. Missing a case becomes something you fix while writing the program.

Reusable code, checked across types

Generic functions and data types let one implementation work with different value types.

Build reusable containers, helpers and transformations while retaining checks on what goes in and what comes out. Where enough information is available, type inference keeps calls and local declarations concise.

Explicit annotations remain available where they clarify the contract or resolve ambiguity.

Pure calculations. Explicit interactions.

Panackelty makes the boundary between calculation and interaction part of the function’s type.


pure twice(n: Nat): Nat {
  n * 2
}

A pure function cannot print, read files, inspect the clock or call effectful code. The compiler enforces that boundary.

You can still use loops and local mutable variables inside a pure function. Purity controls interaction with the outside world; it does not force every algorithm into the same shape.

Pure functions can still fail—for example through an invalid index. Purity is an effect guarantee, not a promise that execution cannot trap.

Pass behaviour without losing its contract

Functions can accept named functions as typed callbacks.

The callback’s type describes its arguments, result and whether it is pure, effectful or asynchronous. Those distinctions remain checked when the callback is called.

That lets collection operations and reusable algorithms accept behaviour without weakening the surrounding effect rules.

Current callbacks use named function references. Capturing closures and references to generic functions are not yet supported.

Transform data without changing the original

Arrays, maps and sets support everyday data processing. Their persistent operations produce new values while preserving the original collection.

Use iteration, map, reduce and stable sorting to organise and transform data. Method-style calls keep operations close to the values they act on.

Keeping earlier values intact makes it easier to follow how a result was produced and reason about what each part of a program can change.

Make absence and failure visible

A lookup might find nothing. A file operation might fail. Those possibilities belong in the function’s contract.

Option[T] represents a value that may be absent. Result[T,E] represents either a successful value or an error.


pure value_or(option: Option[Nat], fallback: Nat): Nat {
  match option {
    Some(value) => value,
    None() => fallback
  }
}

Callers can inspect the result, choose a fallback or pass the error onward.

Panackelty currently handles recoverable failures through explicit results. It has no try/catch, and runtime traps remain distinct from returned errors.

Async/await as a language feature

Declare asynchronous functions with async and use await to compose operations that may suspend.

Inputs, results and error values remain typed. Asynchronous functions can call other asynchronous functions, keeping a sequence of waiting operations readable.

The runtime supports suspension and resumption, while the compiler checks where asynchronous calls are allowed.

An async declaration does not automatically launch work in the background or make a calculation parallel. General-purpose task spawning is not available yet.

Text and bytes, with an explicit boundary

Work with Unicode strings, interpolation and familiar text operations. Use byte values when the contents are binary.

String indexing follows Unicode code points. Explicit UTF-8 encoding and decoding make it clear when a program crosses between text and bytes.

That distinction matters for file formats, command output and network protocols. Code points are not always the same as the visible characters a reader perceives.

Native alpha.11 · experimental

Tools and runtime

Practical access to the host

The native toolchain provides typed paths, filesystem operations, process execution and elapsed-time measurement.

Read and write files, inspect directories and work with temporary resources. Run commands with explicit arguments, working directories, environment overrides, output limits and timeouts.

Duration represents exact nanosecond durations. Monotonic Instant values support elapsed-time calculations and deadlines.

Host APIs expose expected failures through structured results. Availability depends on the runtime: the browser playground does not offer unrestricted filesystem or process access.

Bounded TCP clients and servers

Native alpha.11 puts async/await to work with TCP request/response operations and finite servers with concurrent handlers.

Connection limits, payload bounds, deadlines and shutdown rules are explicit parts of the API.

This is a working, experimental networking foundation. DNS, TLS, HTTP and indefinitely running servers are not included. Raw TCP is unavailable in the browser playground.

Build programs across files

Imports let you organise code into separate source files and use the standard library.

Logical paths such as stdlib/path and project/shared/module keep imports independent of where the toolchain is installed.

Alpha.11 combines ordinary imported declarations into a program-wide namespace. The richer namespace and package model described below is upcoming work.

A compiler you can question

Checking a program is useful. Understanding the reason behind a decision is useful too.

Alpha.11 introduces panack explain for selected compiler reasoning, including supported natural-number subtraction proofs and local call/await effect boundaries.

Use it to inspect the facts behind a supported decision instead of treating the compiler as a black box.

Its scope is currently limited. It does not explain every diagnostic or certify an entire program.

Check, run, compile and inspect

The panack command supports source checking, execution, compilation to saved bytecode and disassembly.

Optional source maps and panack locate connect a bytecode instruction with its source. This provides a foundation for investigation; it is not yet an interactive debugger or an automatic source-level stack trace.

The testing library provides assertions, reporting and helpers for command and filesystem tests.

Programs execute through a bytecode VM that verifies bytecode before running it.

Written in the language it compiles

Panackelty’s compiler is written in Panackelty.

The project uses its own language to implement a substantial compiler, alongside bootstrap and reproducibility checks. The language is exercised by the toolchain that develops it, not only by demonstration programs.

Testing includes compiler, runtime and complete-program checks. Published native coverage measures the C VM; measured .panack source coverage remains an explicit development priority.

Upcoming · roadmap

What’s coming next

Panackelty’s next steps aim to make larger programs easier to organise, networked applications easier to build, and compiler decisions easier to understand.

These are roadmap items, not promises about the current download. Scope and sequencing may change; no release dates are implied.

Namespaces that let projects grow

Namespaces will give declarations an explicit home, making it easier to organise APIs and use code from different modules without name collisions.

The work includes checked naming and visibility boundaries, so a module can expose its intended interface while keeping implementation details internal.

Namespace implementation is progressing in development. Executable namespace support is not included in alpha.11.

Reusable modules and packages

The module and package programme extends code organisation beyond importing files into one application.

The goal is a clear structure for reusable libraries, explicit package relationships and reproducible builds. Programs should be able to depend on well-defined interfaces without depending on an author’s checkout layout.

The work covers package structure, dependency handling, installation and acceptance through real applications.

HTTP built on the networking foundation

TCP supplies the transport. HTTP adds the request, response and protocol behaviour that many applications need.

Planned HTTP client and server packages will build on the language’s typed results and asynchronous execution, with explicit limits and failure handling.

This should open a more practical path towards service clients and small networked applications. The exact supported protocol scope belongs to the package design; HTTP, HTTPS and production-server capabilities should not be assumed from today’s TCP support.

More insight from the compiler

The current explanations are a starting point.

Planned work explores richer explanations of checked types, effects and proof obligations, alongside clearer connections between source code and generated instructions.

The aim is to help answer practical questions:

  • Why was this operation rejected?
  • Which fact allowed the compiler to accept it?
  • Where did an effect enter this calculation?
  • Which source expression corresponds to this instruction?

Broader explanations and source-aware runtime diagnostics remain separate pieces of work, each with its own limits and acceptance evidence.

Better evidence about what our tests exercise

Measured .panack source coverage is a high-urgency roadmap item.

The goal is to show which compiler, library and tooling lines, functions and branches execute during testing—including eligible code that is never reached.

That evidence will help target missing tests. Coverage numbers will support testing decisions; they will not substitute for meaningful assertions.

Faster development feedback

Compiler and build performance are ongoing engineering priorities.

The work starts with reproducible measurements, then targets the costs that matter during editing, checking, testing and packaging. Improvements must preserve correctness, bootstrap guarantees and required validation.

We will publish measured results with their workload and limitations rather than promise a speedup for every program.

Explore what works today. Follow what comes next.

Try the language in your browser, install the native preview, or follow the roadmap and help shape the next stage.