Five years ago I had a bizzare dream, should probably ignore it, but I didn’t, and it took me 5 years to get here.

I have been building a programming language called Pie.

For a while, Pie was just a private project of mine, like hundreds of others. No GitHub repo or anything.

So before I publish it, probably sometime between June 19th and June 30th, I wanted to write down what Pie is, what works, what is missing, and why I made it in the first place.

The repo will be here when it is public:

https://github.com/realslugbrain/pie

Pie is not production-ready, it’s not here to replace Rust, Python, Go, or whatever else people use to write actual software.

Pie is my attempt at a native programming language that tries to be readable and friendly, while still giving the programmer low-level control when they need it.

Also, the syntax can still change overnight. Nothing is set in stone until v1.0, and v1.0 is not exactly around the corner either.

What Pie looks like

Here is a small snippet Pie program:

struct User:
    id: int
    name: string
end

fn display_name(user: &User) -> string:
    if user.name == "":
        return "anonymous"
    end

    return user.name
end

fn main() -> int:
    user: User -> new User(id: 1, name: "Ada")
    println(display_name(&user)) #outputs "Ada"
    return 0
end

Blocks start with : and end with end, there are no semicolons, I HATE semicolons, if you hear otherwise from me, the government has replaced me with a clone, they’re ugly, useless and annoying.

Variables are immutable by default:

fn main() -> int:
    mut count: int -> 0

    while count < 5:
        println(count)
        count += 1
    end

    return 0
end

The general idea is that small programs should look like small programs, and other languages make it really hard and ugly.

Why another language?

It’s the obvious question, why not Rust or C, or literally anything else?

Mostly because I wanted to, a dream I had has not stopped tormenting me since I saw it, just saw name: int -> 1 and ever since this one piece of syntax is pushing me to continue.

There are already many good languages. I am not building Pie because I think every existing language is bad. I am building Pie because I wanted to explore a specific shape of language, i mean, I don’t hate C (which is what I use most), but come on, it’s older than some of the countries! Memory management in Rust? not a fan, Go? it’s good, almost felt at home, but none of the languages did what I wanted well

I am not claiming Pie is better than any other language. Right now it is worse than all of them in almost every practical way. The goal is not to invent strange, quirky syntax just for the sake of it. The goal is to make a language I would actually want to use for tools, small servers, experiments, and native programs.

Safety by default (hopefully)

Pie has ownership. References can be shared or mutable. Unsafe operations have to be inside an unsafe block.

A simple mutable borrow looks like this:

fn rename(user: &mut User, name: string):
    user.name = name
end

fn main() -> int:
    mut user: User -> new User(id: 1, name: "Ada")

    rename(&mut user, "Grace")

    println(user.name)
    return 0
end

Raw pointers exist too, but why would you use it?:

fn main() -> int:
    mut n: int<32> -> 10

    unsafe:
        ptr: *int<32> -> &raw n
        *ptr = 123
    end

    println(n)
    return 0
end

I don’t want Pie to pretend that low-level programming is simple. It is not. But I do want the language to make it obvious.

Safe code should look safe, and unsafe code shouldn’t.

Errors as values

Pie uses Result and Option for recoverable errors and missing values.

fn parse_user_id(text: string) -> Result(int):
    if text == "":
        return Err("empty user id")
    end

    return Ok(text as int)
end
# ^ look how beautiful this is
# no other language can do it as easily

fn load_user(text: string) -> Result(User):
    id: int -> parse_user_id(text)?
    return Ok(new User(id: id, name: "Ada"))
end

The ? operator unwraps the value if it is Ok, or returns early if it is an error.

There is also a prefix version:

id: int -> try parse_user_id(text)

I like both forms. Maybe one of them is a bad idea. That is the kind of thing I’d love feedback on.

Regions

Pie also has region blocks for scoped allocation.

region request:
    tokens: list(Token) -> tokenize(source)
    ast: Ast -> parse(tokens)?
    check(ast)?
end

The idea is simple, just allocate a bunch of short-lived stuff together, then release it together, really nice.

This is meant for things like parsers, request lifetimes, compilers, frame allocators, and temporary work.

This part is still early and not implemented. I like the idea a lot, but liking an idea is not the same thing as implementing it correctly :P

The quantum bool

At some point I added this:

a: bool -> maybe

This does not mean booleans are 1.5 bits now.

it just chooses true or false on runtime.

Is this useful? Probably not.

Did I add it anyway? Yes, expect more.

What exists today

Of course it’s not just a doc file :P

The compiler exists. It’s currently written in C11 and targets native Linux x64.

A decent amount already works:

  • lexer and parser
  • semantic checking
  • intermediate representation
  • native Linux x64 code generation
  • structs and enums
  • match statements and match expressions
  • lists, maps, tuples, and strings
  • functions and closures
  • generic functions
  • basic ownership and borrow checking
  • explicit unsafe blocks
  • raw pointer operations
  • Result, Option, ?, and try
  • commands like pie new, pie run, and pie check

There is also a test suite with around 180 Pie files as of writing this.

That sounds more impressive than it is. A lot of things work, but many of them are fragile, please dont look at the source code /j.

Still, it compiles real Pie code into real executables!, and that feels pretty good.

What does not exist yet

A lot, Pie is not ready for real users.

Some missing or unfinished things:

  • the standard library is tiny
  • the package system barely exists
  • the borrow checker is not finished
  • the language spec is unstable
  • cross-platform support does not exist yet
  • the module system needs work
  • there is no formatter
  • there is no docs generator
  • there is no LSP
  • there is no profiler
  • there is no debugger
  • there is no package registry
  • there is no war in Ba Sing Se
  • there are definitely compiler bugs

The first public release will be very experimental, you will be able to write some programs in it and some stuff that should work will throw errors.

That is fine. That is why I want people to see it.

Why was it private for so long?

I did not use Git for most of the early work, dummy mistake on my part, and this is like a 5th rewrite of Pie.

I started using Git for this project only recently, and some of the commits are things like:

new
tttt
maybe works
aaaaaaa

So when the repo goes public, it will not have a history going back to the first lines of code, it will start as a public import around v0.1.0.

I know that is not ideal, but the alternative was pretending I had been a responsible adult :P

For most of its life, Pie was just me playing with syntax ideas, writing compiler code, and trying to make my dream come true.

At some point the private mess became a slightly larger private mess, so now I wanna turn it into a public mess.

A bigger example

Here is a café example that uses enums, structs, matching, maps, and closures:

struct Order:
    id: int
    customer: string
    item: Drink
    total: float
end

enum Drink:
    case Coffee(string)
    case Tea(string)
    case Water
end

fn get_price(d: Drink) -> float:
    match d:
        case Drink.Coffee(kind):
            if kind == "Espresso":
                return 2.50
            end

            return 3.25

        case Drink.Tea(name):
            return 2.75

        case Drink.Water:
            return 1.00
    end
end

fn main() -> int:
    inventory: map(string, int) -> {
        "Beans": 50,
        "Milk": 20,
        "Cups": 100,
        "Tea": 50
    }

    deduct: fn(string, int) -> void -> fn(item: string, amount: int) -> void:
        current: int -> inventory.get(item)
        inventory.put(item, current - amount)
    end

    drink: Drink -> Drink.Coffee("Espresso")
    subtotal: float -> get_price(drink)
    total: float -> subtotal * 1.08

    match drink:
        case Drink.Coffee(kind):
            deduct("Beans", 5)
            deduct("Cups", 1)

        case Drink.Tea(name):
            deduct("Tea", 1)
            deduct("Cups", 1)

        case Drink.Water:
            deduct("Cups", 1)
    end

    order: Order -> new Order(
        id: 101,
        customer: "Alice",
        item: drink,
        total: total
    )

    println("Order #", order.id, " for ", order.customer)
    println("Total: $", order.total)

    return 0
end

I like this example because when I first wrote it, it did not compile, then I spent about a week or two making it work, so now mostly all I do is write code that doesn’t work, make it work, then repeat.

What I want from the first release

I am not expecting people to immediately use Pie for serious work, or even use it for unserious work. The first release is mainly for feedback.

I want people to tell me things like:

  • does the syntax feel readable?
  • which parts look annoying?
  • which parts look unnecessary?
  • which parts look too much like other languages? (and is it fine?)
  • which parts are confusing?
  • what should be removed?
  • what should be implemented next?
  • what would make you actually try it?

I especially appreciate feedback from people who like compilers, programming languages, Rust, Zig, C, or language design in general.

So.. What’s next?

Before publishing the repository, I want to clean up a few things:

  • write a proper README
  • add more examples
  • document how to build the compiler
  • don’t mess up

The first version will probably be something like v0.1.0, because this is the first public release, and even if a lot of code already exists, it’s still pretty early.

Closing

Pie is the biggest project I have ever worked on.

I don’t know if Pie will become useful, and I don’t know if anyone else will care about it. It’s the project of my life, just want to share it.

I hope that people see something in Pie that I might have missed, and will share their opinion here

Expect more posts in the future :)