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.
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.
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" ) ] )
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
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)
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" }
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
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()
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
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.