talk_dev's Journal

Talk of the Town

Today's Typecheck
talk_dev posted a new release

TAFT SWORN IN AS CHIEF JUSTICE

The former President becomes first to hold both offices. Also, packages arrive with locked dependencies and first-class tests, while equality, matching, ownership, core APIs, and editor repairs fill in the language around them.


This issue follows Talk from a source file into a working project. The language can now create, install, build, run, and test packages without flattening their module boundaries. At the same time, the code inside those packages gets structural equality, richer patterns, tuple access, explicit cloning, practical Unicode and byte tools, and an editor that can suggest the conservative repair instead of merely pointing at the damage.

Packages Get an Address and a Lockfile

A directory with package.tlk is now a real Talk package. Its manifest can declare one library, multiple binaries, and direct dependencies from Git revisions, checksum-verified tarballs, or local paths. Dependencies compile as external modules, so public API boundaries survive instead of dissolving into one source workspace.

talk new creates a runnable package, talk install resolves and materializes its graph, and talk update deliberately advances selected refs. The checked-in package.lock records exact commits and checksums; talk build, talk run, and talk test refuse a stale graph and support offline use when the cache is complete.

A Package Manifest
Package(
	name: "word-watch",
	version: "0.1.0",
	builds: [
		.lib(from: "src/wordwatch.tlk"),
		.bin(
			named: "watch",
			from: "src/main.tlk"
		)
	],
	dependencies: [
		.path(
			package: "token-kit",
			path: "../token-kit"
		)
	]
)
Tests File Machine-Readable Copy

talk test --json now reports suite status, failures, captured output, and a record for every executed test. --filter NAME selects one exact test before its block runs, giving editor integrations a clean route to file-level and single-test execution.

The new neotest-talk adapter uses that route in Neovim, and talk setup nvim installs it with the existing runtime files. Package testing also got broader: bare discovery now checks both tests/**/*.test.tlk and src/**/*.test.tlk, while explicit files retain package imports and dependency context.

Writing tests is for dweebs. - The Compiler Desk
Equality Writes Its Own Witness

Value structs and enums now derive same-type Equatable when every stored component is Equatable. Structs check fields in decl order; enums compare tags before the selected payload; both stop at the first mismatch. Generic and recursive values participate, while heap structs and cross-type comparisons still require an explicit conformance.

Equality inference now prefers the same-type application when a leading-dot case needs context, without taking away concrete cross-type conformances.

struct Point {
	let x: Int
	let y: Int
}

enum Choice<T> {
	case none
	case value(T)
}

Point(x: 1, y: 2) == Point(
	x: 1,
	y: 2
)

Choice.value(3) != Choice.value(
	4
)
Patterns Read More Than Tags

Enum payloads may now carry labels, and those labels are checked in declaration order at both construction and match sites. Match arms also accept character and string literals, including escapes, Unicode text, empty strings, and or-patterns. String matching compares UTF-8 bytes and remains deliberately non-exhaustive without a catch-all.

The checker now considers the whole set of top-level case names before choosing among enums that share a spelling such as .ok. Nested tuple and record patterns view through borrowed enum occurrences, while ownership remains projection-specific so borrowed binders and owned payloads get the right cleanup.

enum Token {
	case word(text: String)
	case punctuation(mark: Character)
}

func keyword(word: &String) -> Int {
	match word {
		"func"|"let" -> 1,
		"if"|"else" -> 2,
		_ -> 0
	}
}

let token = Token.word(
	text: "match"
)
let text = match token {
	.word(text: value) -> value,
	.punctuation(mark: '!') -> "bang",
	.punctuation(mark: _) -> "mark"
}
Tuples Number Their Seats

Tuple values now expose positional members such as .0 and .1. Access can chain through nested tuples and works through borrowed receivers, while ordinary destructuring keeps its old meaning. The parser also accepts else if chains in statements, expressions, and chains that begin with if let.

Static methods can once again construct and return their own nominal type, closing a checker gap that made factory methods look like unit-returning initializers. Together these changes remove small but persistent detours from everyday data-shaping code.

func direction(point: (Int, Int)) -> String {
	if point.0 > 0 {
		"right"
	} else {
		if point.0 < 0 {
			"left"
		} else {
			"center"
		}
	}
}

let nested = ((1, 2), 3)
let second = nested.0.1
Cloning Becomes a Method

Copy and CheapClone now promise a real clone() -> Self method. The compiler supplies it for marker conformances: copy values duplicate without runtime work, while cheap clones retain shared storage. A declared method wins when a type provides one, and affine values without either conformance still have no clone member.

Arrays can now be cheaply cloned without asking their elements to clone, because retaining the shared buffer does not visit those elements. Payload-free enums are classified as copyable runtime tags, so a borrowed tag can flow into an owned parameter or field without an unnecessary consume.

struct BoxedText {
	let value: String
}

extend BoxedText: CheapClone {}

let original = BoxedText(
	value: "hello"
)
let duplicate = original.clone()
let copied = 42.clone()
Core Learns the Lexer Trade

Porting lexer work exposed the missing pieces in core. Character now classifies whitespace, letters, numbers, alphanumerics, ASCII digits, and hex digits from generated Unicode 17 data. String.scalars() and Substring.scalars() provide the lower-level code points when a grapheme is too large a unit.

Core also gains Result<Success, Failure>, normal equality and ordering for Byte, and an in-place Array.swap backed by a typed VM swap instruction and copy-on-write uniqueness. These are modest APIs, but together they let systems code stay in Talk instead of reaching for private conversions and load-store tricks.

for ch in "A lambda 123" {
	if ch.is_whitespace() {
		print("space")
	} else {
		if ch.is_alphabetic() {
			print("letter")
		} else {
			if ch.is_numeric() { print(
				"number"
			) }
		}
	}
}

for scalar in "1\u{301}".scalars() {
	print(scalar)
}
let values = [2, 3, 1]
values.swap(0, 2)

let result: Result<Int, String> = .ok(
	values.at(0)
)
let first = match result {
	.ok(value) -> value,
	.error(_) -> 0
}

let bytes = "az".utf8()
assert(bytes.at(0) < bytes.at(1))
The Compiler Desk sucks. - The Testing Desk
Editors Bring Suggested Repairs

Diagnostics now carry stable parser and type codes plus enough structured context for clients to act on them. The LSP turns that structure into conservative quick fixes: adding or removing call arguments, correcting member spellings and payload labels, filling missing match arms, adding effect or protocol requirements, and removing unreachable or duplicate syntax.

The actions are attached to the diagnostic that justified them and are withheld when the compiler cannot identify one safe textual rewrite. Type mismatch messages now name the actual context - argument, return, assignment, branch, condition, pattern, or array element - and goto-definition can descend through optional, generic, tuple, record, unique, and nominal-path annotations.

permalinkrecent entriesarchive
The Daily Talk - What's New in Talk - Release Notes for July 11, 2026