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.