🦉talktalk. v0.1.1
A bi-annual review of one person's compiler

talktalk.

talktalk is a small, fully-typed language that kind of looks like Swift or Rust or Go if you squint really hard.

It wants to feel familiar and cozy, but still pack modern power — type inference, sum types, protocols, algebraic effects, and a little IR it gets lowered into before a very little interpreter runs it.

It's mostly a way for me to understand compilers. You shouldn't use talktalk. But you might enjoy perusing it.

Version 0.1.1
License MIT
Stability ha. no.
hello.tlk talktalk
hello.tlk talktalk
1 2 3 4 5 6 7 8 9 10 11 12
01

Goals & non-goalswhat this thing is trying to be, and isn't

Goal

01Learning stuff

This is by far the biggest goal. I didn't super understand all the ins and outs of compilers. I still don't, but at least I have a way to learn now.

Goal

02Fully typed everything

Types are cool. You can add them when you want and leave them off when you don't.

Goal

03As much type inference as possible

I don't know if it's a good idea. It's probably not. I just think it's neat.

Goal

04Familiar-ish syntax

Haskell/ML-y syntax is beautiful. I hate it. This one has braces.

Goal

05Algebraic effects

Effects are like exceptions, but also like coroutines, but also like dependency injection. I'll explain later. Maybe.

Goal

06Syntax highlighting color schemes

I feel like making a full programming language is the only way to do this. Right? Right? Don't answer.

Non-goal

01Blazingly fast performance

I'm probably not gonna litter the codebase with sleeps. But I'm allowed to if I want.

Non-goal

02Soundness & decidability

Is this even possible? I saw a YouTube video that said it's not.

Non-goal

03Getting anyone to use it

Why would I? PHP exists. Use that.

02

The language tourwhat you can actually write in it

Primitives

Some types you've probably seen before. Tuples and records are maybe a little less common but we've got 'em.

primitives.tlk talktalk
1 2 3 4 5 6 7

Variables & expressions

The last expression in a block is its value. No semicolons required; no semicolons denied either.

variables-expressions.tlk talktalk
1 2 3 4

Functions

Plain, boring, good. Trailing blocks work for callback-y things too.

functions.tlk talktalk
1 2 3 4 5

Type annotations

Annotate when you like. Everything gets checked either way.

type-annotations.tlk talktalk
1 2 3

Generics & inference

Polymorphic by default. Spell things out if you want.

generics-inference.tlk talktalk
1 2 3 4
generics-inference.tlk talktalk
1 2
// Or be explicit:
func identity<T>(x: T) -> T { x }

Closures

Closures capture their environment. They're values, so you can pass them around.

closures.tlk talktalk
1 2 3 4 5 6 7 8 9 10

Structs

Structs are fields plus methods. init blocks let you customize construction.

structs.tlk talktalk
1 2 3 4 5 6 7 8 9 10

Enums & pattern matching

Sum types with associated values. Match on them with if-let, let-else, or full match expressions.

enums-pattern-matching.tlk talktalk
1 2 3 4 5 6 7 8 9

Protocols & associated types

Protocols can have required methods, default methods, and associated types with their own bounds.

protocols-associated-types.tlk talktalk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Algebraic effects

Effects are declared with a leading ' and handled by @handle blocks. Useful for exceptions, async, DI — pick your poison.

algebraic-effects.tlk talktalk
1 2 3 4 5 6 7 8 9 10 11

Effects as exceptions

Are effects just weird functions? Kind of. But you can also use them as exceptions.

effects-as-exceptions.tlk talktalk
1 2 3 4 5 6 7 8 9 10 11 12 13

Modules

Modules span files. This one isn't runnable in the browser, but the CLI handles it fine.

modules.tlk talktalk
1 2 3 4 5 6
// Exports.tlk
public let a = "we can export this string"

// Main.tlk
import { a } from ./Exports.tlk
print(a)

A rough little HTTP server

There's already some rough HTTP stuff. Don't look at it too hard.

a-rough-little-http-server.tlk talktalk
1 2 3 4 5 6 7
let http = HTTP.Server()

http.get("/",       func() { "hello from talk" })
http.get("/health", func() { "ok" })

print("Listening on http://localhost:3000")
http.run(3000)
03

How it worksa compiler in six polite movements

Step 01
Lex
source → tokens with start/end positions and kinds (Ident, StringLit, Comma…).
Step 02
Parse
tokens → AST. Recursive descent, Pratt precedence for expressions.
Step 03
Resolve
names → symbols. Replace Name::Raw(string) with Name::Resolved(symbol, string).
Step 04
Check
the big one. infer + check types. constraint generation, unification.
Step 05
Lower
TypedAST → lil IR. basic blocks, SSA-ish, explicit allocs.
Step 06
Interp
lil IR → lil interpreter. we just walk it. blazing? no. legible? yeah.

In · source

let a = 1 + 2

Out · Lex

Let@0..3  Ident("a")@4..5  Eq@6..7  Int(1)@8..9
Plus@10..11  Int(2)@12..13  Eof@13..13
04

What's missinga short and honest list

MISSING

Real I/O

You can print to stdout. That is, uh, literally it. Seems like something one might want more of.

MISSING

Standard library

No hashmaps, no sort, no JSON. There also isn't an un-standard library, so I'm not sure why I put that in quotes.

MISSING

Explicit mutation

Everything is mutable right now. It's chaos. The sky is crumbling. I have a Looney Tunes umbrella.

MISSING

Mutable arrays

No push yet. If you are a functional programmer with a cool leather jacket, please enjoy this page.

MISSING

Concurrency

But concurrency isn't parallelism! So parallelism? Also no.

PARTIAL

Visibility modifiers

Everything public, all the time, baby. public exists as a keyword, mostly for vibes.

PARTIAL

Real docs

You are looking at it. Kind of. (Hi.)

MISSING

LSP & tooling

Syntax highlighting in a browser is fun. Real tooling is hard. Someday.

NON-GOAL

A better name

It's fine. I think it's fine. I'll stop saying it now.

05

Install & getting startedthree minutes, tops

~/codezsh
$ brew install talktalk/tap/talk
$ talk --version
talktalk 0.1.1 (interpreter, debug)
 
$ echo 'print("hi")' > hi.tlk
$ talk run hi.tlk
hi
 
$ talk --help
run <file> run a .tlk file
lower <file> print the lowered IR
fmt <file> format in place
repl a friend you can talk to
first stepsdo these in order
  1. Install the binary. Homebrew on macOS, a prebuilt binary for Linux, or build from source with cargo install --path .
  2. Run the REPL. talk repl. It's low-key my favorite part.
  3. Read the tour above. Most of what the language does is covered in under five minutes.
  4. Open the docs for reference: language spec, pipeline internals, effects, and (eventually) a standard library.
  5. File a bug when something breaks. It will. That's how learning happens.