Status and roadmap
This page summarizes the release history and the future work that is actually stated in the repository. The release by release detail lives in the changelog on GitHub.
Current status
Section titled “Current status”The current release is 1.11.0, and Dusk is self-hosting. The compiler runs the whole pipeline: it lexes, parses, resolves names, type checks, monomorphizes, and emits code, backed by a golden and unit test suite that stands at 907 records passing under testrun tests/goldens.manifest. The standard library and the multi module sample both build and run, and Dawn stays byte compatible with 10 of 10 offline checks green. Beyond the compiler, the official language server, dusk-lsp, is written in Dusk and drives the same compiler for its answers, so an editor’s diagnostics are the compiler’s own.
1.0.0 declared the bootstrap done: the compiler written in Dusk became canonical, having built itself to a byte identical result three stages deep at 0.9.4. The language surface froze at 0.5.4 for that rewrite and stayed still through 1.0.0, then reopened and grew across the 1.1 through 1.8 releases. At 1.3.1, “the retirement”, the original Rust compiler was deleted from the repository, so the repo is now pure Dusk: the compiler (root file compiler/dusk.dusk), the Dawn package tool, and the native test runner are all written in Dusk and build with the dusk compiler itself. The Rust implementation is archived at github.com/choice404/dusk-rust, a frozen full-history archive at tag v1.3.0 and the reference for the surface through 1.2.0. See the Unicode guide and the collector reference for parts of that surface, and Concurrency for the substrate the async line rides on.
Release history
Section titled “Release history”Development has proceeded line by line, each a minor version series with one theme.
0.1.x: the core language, end to end
Section titled “0.1.x: the core language, end to end”0.1.0 delivered the core language through the whole pipeline: paradigm directives gating procedural, functional, and OOP features per file, structs, methods, enums with exhaustive match, interfaces with vtables, closures, monomorphized generics, functional builtins, do notation, errors as values under the must-handle rule, explicit memory with alloc, free, and defer, a module system with a stdlib seed, and a golden test suite compiling and running every example.
The point releases filled in the planned core:
- 0.1.1: correctness and diagnostics,
charsemantics, errors as values lowered end to end, per file source tracking in diagnostics. - 0.1.2: pointer receivers for methods and the
usingAllocatorinterface working end to end, withHeap,FixedBuffer,Arena, andDebugin the stdlib. - 0.1.3: qualified call syntax,
std.map(then a string keyedMap<V>written in Dusk, generic over its key asMap<K, V>since 1.5.2), and file I/O withread_fileandwrite_file. - 0.1.4: console input and parsing,
read_line,read_all,parse_int,parse_int_radix,parse_float, and thestd.iocompositions. - 0.1.5: formatted printing,
printandprintlntake a format string whose{}holes expand at compile time into typed prints.
0.2.x: memory safety
Section titled “0.2.x: memory safety”Releases 0.2.0 through 0.2.6 built the memory safety story described in the memory guide and the memory reference:
- 0.2.0: mutable strings,
StringBuilder,concat, and thecstrbuiltin. - 0.2.1: generational references, the runtime foundation. A managed
*Tis a fat pointer carrying a remembered generation checked at every dereference, so a use after free, a double free, or a stale pointer to a reused block faults instead of corrupting memory. The thin layer,*raw Tand*void, landed alongside. - 0.2.2: single owner pointers, the static half. The checker tracks owners and borrows,
movetransfers ownership and invalidates the source, andrefmakes a non owning alias. - 0.2.3: escaping value lifetimes. Returning a slice viewing a frame local array or a closure capturing a frame local is a compile error.
- 0.2.4: the minimal foreign function interface,
foreign "C"blocks calling libc across the raw pointer boundary. - 0.2.5: closed the gaps a specification review found, generation checked
free, bounds checked indexing, the first enforcement of the must-handle rule, interface conformance at call sites, and more. - 0.2.6: hardened the whole line one level deeper, type sized
alloc(), integer and float widths tracked soint32 + int64is a compile error, immutability covering element and field stores, the binding level must-handle rule, module private name isolation, andDisplaygated printing.
0.3.x: concurrency
Section titled “0.3.x: concurrency”Releases 0.3.0 through 0.3.3 built concurrency in four phases, each covered in the concurrency guide:
- 0.3.0: threads.
spawnstarts an OS thread running a lambda whose captures copy into a private heap environment,joinwaits and retires the handle, the generational heap is thread safe, andstd.concurrent.atomiccarries the sequentially consistent counter. - 0.3.1: channels.
std.concurrent.channelcarries a bounded, thread safe queue, with ownership moved across threads throughchan_send(c, move(p))and the sender’s name dead at compile time. - 0.3.2: mutexes and condition variables.
std.concurrent.syncshipsMutexandCondvar, with every classic pthread misuse turned into a named fault. - 0.3.3: the thread pool and the async substrate, the non blocking and timed channel operations, the
submitbuiltin over a global worker pool, andpool_start,pool_shutdown, andncpuinstd.concurrent.pool.
0.4.x: async
Section titled “0.4.x: async”Releases 0.4.0 through 0.4.4 built the async line on top of the 0.3.x substrate, covered in the async guide:
- 0.4.0: futures and the event loop, the first phase.
std.async.futureships a one shotFuture<T>completed from any thread,std.async.loopruns the single threaded event loop, andstd.async.timeaddssleep_async. An await that provably cannot finish aborts by name instead of hanging. - 0.4.1: the epoll reactor, the second phase.
std.async.ioturns file descriptor readiness into a one shot future through one epoll thread, withreadable,writable, a non blocking pipe and byte surface, and the reactor lifecycle wired into the deadlock gate. - 0.4.2: the
async func,await, andasync_runkeywords, which compile async code to a state machine over a heap frame. The same release lands the complete operator set, the bitwise family, compound assignment,++and--,**,|>,..=, and a thirteen level precedence ladder (see the operator reference), and hardens the escape check and interface boxing. - 0.4.3: networking and async sugar.
std.async.netputs TCP on the reactor’s readiness futures with IPv4 dotted quads and no name resolution,chan_recv_asyncturns a channel receive into an awaitable, and genericdocomposes over any monad instead of only fully ground binds. - 0.4.4: a second reactor platform and hardening. The reactor moves behind a six function poller seam with a
kqueuebackend beside the epoll one, written but unverified until a BSD or macOS runner exercises it,SIGPIPEis ignored process wide, every blocking syscall retries onEINTR, and file descriptor exhaustion surfaces as a named error instead of a leak.
0.5.x: soundness, memory, and text
Section titled “0.5.x: soundness, memory, and text”Releases 0.5.0 through 0.5.4 hardened the language and grew the standard library ahead of the bootstrap:
- 0.5.0: interprocedural escape analysis, the ledger. Escape checking became summary based across calls, with every function carrying a summary over what it returns, reads through, flows into, and sinks, so a frame view laundered out through a call, a store, a channel send, a closure, or a pointer alias is caught instead of slipping past.
- 0.5.1: the collector.
collector<T>opts a value into a second managed heap, a conservative mark and sweep collector beside the generational one, sharing the same block header and dereference check. Nothing is collected unless you mint it, and the collector is confined to the main thread. See the collector reference. - 0.5.2: Unicode strings. The
runeprimitive carries one Unicode scalar value,r'...'and\u{...}literals spell codepoints, a string literal must be valid UTF-8, andstd.unicodedecodes and encodes UTF-8 in pure Dusk. See the Unicode guide. - 0.5.3: the standard library.
IO<T>became a true lazy monad over a collected thunk,std.functional.resultaddedResult<T, E>with a monad block, andstd.loggingadded leveled logging to stderr. - 0.5.4: the audit and the freeze. A hardening pass reserved the unsigned integer widths, gated
implbehind@paradigm oop, extended the must-handle rule toerrorparameters, added caret diagnostics, and froze the language surface for the bootstrap.
0.6.x through 0.9.x: the bootstrap
Section titled “0.6.x through 0.9.x: the bootstrap”Releases 0.6.0 through 0.9.4 rewrote the compiler in Dusk itself, one pipeline stage at a time, with the language surface held still under the freeze. A parity gate, tools/differential.sh, held each stage to matching the Rust compiler byte for byte, and tools/pyramid.sh climbed the stage ladder toward a fixpoint:
- 0.6.0 opens the line with the front end scaffold, the lexer and the diagnostic renderer in procedural Dusk, with
dusk1’slexandscandumps matching the seed’s across every file inexamples/andlib/std. 0.6.1 records theelse ifchain in the spec, a shape the parser always accepted. - 0.7.0 gives dusk1 the parser, so it builds the same AST the seed does.
- 0.8.0 through 0.8.3 port the judgment: name resolution and type checking, then the interprocedural escape summary, then monomorphization and the ground type pass, until dusk1’s verdict agrees with the seed’s across the whole sema corpus with no exclusion left.
- 0.9.0 through 0.9.4 port code generation: the scalar spine first, then aggregates, then closures and the collector, then the async state machine, until every construct the surface carries lowers under dusk1. 0.9.4 climbs the last rungs of the ladder to the fixpoint, stage1, stage2, and stage3 landing on the identical binary and the identical compiler IR.
1.0.0: the declaration
Section titled “1.0.0: the declaration”1.0.0 declares the bootstrap done. No language surface change and no compiler behavior change: the compiler written in Dusk becomes the canonical dusk compiler. The Rust compiler stays on for now as the seed whose one remaining job is rebuilding the first stage from dusk source, a job it keeps until the 1.3.1 retirement. The fixpoint reproduces at the release tag, with the golden suite passing in full against both the first and second self-built stages, and the 0.5.4 surface is the 1.0.0 surface, unchanged start to finish.
1.0.x through 1.4.x: past the bootstrap
Section titled “1.0.x through 1.4.x: past the bootstrap”With the compiler self-hosting, the frozen surface reopened and grew, and the Rust seed was retired. Each release carries one theme:
- 1.0.1: the installed compiler. The canonical compiler gains the same asset search the seed had, a five step probe for
lib/andruntime/:DUSK_HOMEchecked against the specific asset, the directory the running executable sits in, ashare/dusk-langdirectory one level above that, the directoryargv[0]names, and the working directory as a source checkout fallback. A compiler installed atprefix/binbesideprefix/share/dusk-langfinds its assets with noDUSK_HOMEset. - 1.1.0: the byte behind the glyph.
print,println, andprinterrwrite achar, achar[N], and achar[]as their text bytes rather than as numbers, soprintln(s[0])on"hi"printsh. Arunestill prints its codepoint number, by design.for c in siterates a string’s bytes front to back, a string range slices[lo..hi]is bounds checked against the scanned length, andstd.stringgainsstr_from_charsto copy a char slice into a fresh heap string. - 1.2.0: the daily driver.
&&and||short-circuit, string==and!=compare content and+concatenates, the width castsint8throughint64andcharconvert explicitly,breakandcontinuebecome statement keywords gated to@paradigm procedural, comparison closes to the scalars andstringwith named rejections for the rest, and the common runtime faults name the file and line that raised them. - 1.3.0: the native harness. The golden test runner (
testrun) and Dawn (compiler/dawn.dusk) are ported to Dusk, so the whole toolchain is now written in Dusk.std.stringgainsstr_findandstr_contains. No language surface change. - 1.3.1: the retirement. The Rust implementation is deleted from the repository, which is now pure Dusk. The old compiler is archived at github.com/choice404/dusk-rust, a frozen full-history archive at tag
v1.3.0and the reference for the surface through 1.2.0. The canonical compiler root file iscompiler/dusk.dusk. - 1.4.0: the open boundary. A
foreignblock may end its parameter list in...to bind a variadic C function such asprintf, the@linkand@csourcedirectives pull libraries and C files into the link line,std.mathbinds 22 of libm’sfloat64functions with pure Duskpi,e,is_nan, andis_inf,std.randadds an xoshiro256** generator over a heapRng, and float!=is corrected to IEEE 754’s unordered comparison soNaN != xanswerstrue. - 1.4.1: structs across. A C plain struct crosses a
foreignboundary by value, classified and coerced the way clang’s own System V x86_64 ABI places it, eightbyte by eightbyte, register or memory, and checked byte for byte against a clang compiled object on either side of the call. A field the boundary cannot carry is rejected by name rather than quietly mislaid. Two modules land on the reopened boundary:std.fs, files and directories with pure Dusk path arithmetic, andstd.time, UTC clock reads paired with a pure Dusk proleptic Gregorian calendar. - 1.4.2: the callback. A
foreignparameter may be declared with a function type, so a capture free lambda or the name of a top level function crosses as a bare C function pointer, one word with no environment and no trampoline in between, which is the shapeqsortand most C registration APIs actually ask for. A callback that captures a local is refused, since C has no environment to put it in.std.processruns a command and reads its output back, andstd.vectorgainsvec_sort, a stable and deterministic merge sort behind a comparator, alongsidevec_containsandvec_index_of. - 1.4.3: Dusk as a library. The three releases before it carried C into Dusk; this one carries Dusk out. An
export "C" funcis a function a C caller reaches by its own bare symbol, anddusk build --libcompiles a module into a static archive and a generated C header that any C ABI language links against, the module free to omitmainentirely.std.jsonlands on top, a parser and emitter over a recursive enum. See the CLI page for the flag and C libraries for the boundary. - 1.4.4: the boundary hardened. No new surface. The line closes by testing the whole boundary against adversarial input and fixing what that surfaced: fault goldens across every boundary feature, proving a Dusk fault crosses an export as a clean abort rather than corruption; library packaging fixes, so a private helper named for a libc entry can no longer interpose the host’s own call at the static link; and
std.jsonhardening, a nesting depth bound and a number range check turning a pathological document into a named error instead of a crash. Two limitations are recorded honestly rather than papered over: a parsedJsontree has nojson_freeand is reclaimed at process exit, which 1.8.1 later closes, and the archive is static only, not position independent, so adlopenbased FFI cannot load it.
1.5.x: casts, hashing, the generic map, and the string toolkit
Section titled “1.5.x: casts, hashing, the generic map, and the string toolkit”The 1.5 line opens on the scalar surface and continues into a standard library overhaul:
- 1.5.0: the numeric cast. 1.2.0 added an integer width cast and stopped at the integer family. This release opens the whole scalar set:
rune,float32, andfloat64join the cast builtins, and a cast now crosses the integer and float boundary in both directions. A float to an integer saturates, clamping a magnitude beyond the target’s range to its nearest bound and casting a NaN to zero, rather than leaving an out of range input undefined the way C does. A misused cast is therefore deterministic, which is the safety posture the language takes everywhere else. - 1.5.1: the hash builtin.
hash(v)returns a deterministic 64-bit hash over a hashable value, an integer of any width, achar, arune, or astring. A float is refused, since a NaN breaks the coherence between a hash and equality, and a struct or pointer is refused too. It shipped one release ahead of the map that needs it, on purpose, so the previous release’s compiler could still build the standard library that would use it. - 1.5.2: the generic map.
std.maphad been keyed by strings alone since it first shipped; it is nowMap<K, V>, generic over its key as well as its value, with K drawn from the hashable sethashdefined a release earlier. The open addressing, the linear probe, the half full grow, and the insertion order iteration are all unchanged, and a string key hashes and compares by content exactly as before. The compiler itself, the heaviest map user in the tree at more than seven hundred annotation sites, migrated with it and emits byte identical IR afterward, so the map going generic changed nothing the compiler produces. - 1.5.3: the string toolkit.
std.stringgrew up serving the compiler, so it had the parsers, the builder, and the foreign bridge, but not the everyday manipulation set a program reaches for first. This release adds it, twelve functions in pure Dusk over the existing builder with no compiler change and no runtime change:ends_withandstr_rfindsearch from the tail the waystarts_withandstr_findsearch from the front,str_cmporders by unsigned byte value and returns the shapevec_sort’s comparator already takes, so a vector of strings sorts with no glue,trim_start,trim_end, andtrimstrip ASCII whitespace,repeatandreplace_allbuild a fresh string,str_splitandstr_joincross between a string and a*Vector<string>, andto_upperandto_lowerfold ASCII letters alone, leaving a byte at 128 or above untouched so a multibyte scalar survives intact. The split and join pair is whystd.stringnow importsstd.vector, which is the one way this release breaks code that compiled before: nearly every module importsstd.string, so a program that defined its ownvec_lenor its ownVectornow sees the standard library’s too and fails loudly on the duplicate definition. Rename the private copy.
Two spellings moved in this line: every existing map is Map<string, V> now, and a program carrying its own vec_* names renames them. See the standard library overview for the surface as it stands.
1.6.x: comments, the second target, and doc comments
Section titled “1.6.x: comments, the second target, and doc comments”The 1.6 line writes down two things the language never had and opens a path to the browser:
- 1.6.0: the block comment and the second target. Dusk carried only the
//line comment since 0.1.0, and the spec never wrote even that down. This release adds/* */, nesting the way commented out code needs, and gives the spec a Comments section that records both forms. Atools/comment-differential.shproves the feature invisible, rewriting every line comment in the example corpus into block form and asserting byte identical IR. Alongside it,dusk ir --target=wasm32cross-emits the module forwasm32-unknown-wasip1, the shape a wasi toolchain links and the form the browser playground is built from, while every other command keeps the native triple. Aruntime/wasm_shim.ccarries the wasm side of the runtime, andstd.os’s errno read is renamedos_errnoso its old bare name stops colliding with the C symbolerrnoon a target whose libc owns it. - 1.6.1: the doc comment. A block comment that opens with
/**binds to the declaration it precedes, and the newdusk doccommand renders a module’s documentation as markdown or, with--json, as a stable JSON model for tooling. Every fact comes from the declaration itself, so the documentation cannot drift from the code, and where the prose contradicts the signature the command refuses to emit and says why. See doc comments and the CLI.
1.7.x: the debt release and the machine face
Section titled “1.7.x: the debt release and the machine face”- 1.7.0: the debt release. Every entry in the known defect ledger was re-verified against the current compiler rather than trusted from memory, and the verdicts drove the release. The one live soundness hole is closed: a function that wrapped a pointer argument into a returned struct could let a frame view egress unseen, and the built program read a dead frame; the fix raises the escape summary and the alias linker so the escape is caught at the
return. The rest of the ledger is pinned by goldens or documented honestly, including the boundary that makes a hand rolled deep free of a node tree inexpressible until an owning take exists. - 1.7.1: the machine face. The compiler’s diagnostics and its doc model become data a tool consumes rather than text a person scrapes.
dusk check --jsonemits one deterministic JSON document with every diagnostic’s message and precise source span, and the doc model gains a span on every item. These are the two contracts the language server builds on. No language surface changed. See check —json.
1.8.x: the owning take and the stdlib five
Section titled “1.8.x: the owning take and the stdlib five”- 1.8.0: the owning take.
vec_takeandmap_takeremove an element from a container and hand the caller the owner, and the checker’s borrow net around containers settles into one model across every spelling, so freeing a container read directly is rejected and the take is the sanctioned path. A program can now hand write the deep free of a heap tree, withexamples/jsonfree.duskthe acceptance. No parser, codegen, or runtime change: the removal is two standard library generics plus a checker that blesses their results as owners. See owning removal from a container. - 1.8.1: the stdlib five. Five standard library items and no new surface:
std.flagsfor command line parsing,std.setover the generic map,vec_mapandvec_filterbeside the vector sort,std.time’sweekdayand a strictparse_iso8601, andstd.json’sjson_free, the first in-tree caller of the owning take 1.8.0 introduced. Every item is plain Dusk inlib/std/with its examples and goldens; no token, no AST node, no checker rule, no lowering, and no runtime change.
1.9.x through 1.11.0: the marker, the frame, and the container loop
Section titled “1.9.x through 1.11.0: the marker, the frame, and the container loop”- 1.9.0: the owning func and the debt sweep.
owning funcis the declared generalization of the take blessing: a head markedowningdeclares its call result the caller’s own value, so the checker blesses it exactly as it blessedvec_takeby name. The same release finishes the match expression, typing its result and judging its arms on type and ownership with one voice, and closes a batch of checker gaps that reachedclangor the generation backstop.dusk docalso learns to attach a doc comment to aforeignfunction head. See owning functions. - 1.9.1: the adoption.
vec_takeandmap_takenow carry theowningmarker themselves, and the compiler’s by-name recognition of them is deleted, so the blessing rides one declared mechanism.std.vectorgainsvec_fold, the reduce that completes map, filter, and fold over a vector. The one behavioral flip is raise-only: a user’s own unmarked function namedvec_takeormap_takeis no longer blessed by its name and marks itselfowning functo restore it. - 1.10.0: the scoped frame. Block scoping and shadowing become sound the whole way down to code generation, which gains a per-block scope chain in place of its flat per-function table, so a binding that shadows an outer name in a nested block stops clobbering the outer name’s resolution; the defer replay and lambda capture that resolved through the same table are repaired with it. The type checker already resolved these correctly, so no check verdict moves; every repair is a program that checked clean and then printed a wrong value or died in
clang. A binding that would shadow ausingallocator’s name is now rejected. - 1.10.1: the match tail. An arm body that ends in a bare
matchvalue-izes into the enclosing match, so a nested match is the arm’s value with no name to bind it, and three adjacent judgment holes the feature exposed close with it. A form the checker rejected before now lowers. - 1.11.0: the container loop.
foriterates the two standard containers:for x in vover a*Vector<T>, andfor k in mandfor k, v in mover a*Map<K, V>. A pass between the surface type pass and monomorphization lowers each loop into the exact index loop a program would write by hand againstvec_lenandvec_get, ormap_lenand the two new positional accessorsmap_key_atandmap_val_at, so container layout stays in the standard library and code generation never learns it. The length re-reads every iteration, so a container freed inside the body faults at the next iteration’s generation check rather than reading a stale pointer, and theforbinder is now typed, catching a narrowing bind that would have truncated silently. See iterating with for.
The ratchet
Section titled “The ratchet”The split between 1.5.1 and 1.5.2 was deliberate sequencing rather than tidiness. The map uses hash, so hash had to be one release old before the map could reach for it, which keeps the previous release’s binary able to build the current source. That property is the ratchet, and every release in these lines holds it: the v1.5.1 release binary builds the 1.5.2 source, and the compiler it produces passes the full suite.
The stage ladder re-fixes at every release beside it. Seeded with the previous release’s binary, stage1, stage2, and stage3 land on one shared binary hash and one shared compiler IR hash, with the collapse, fixpoint, and determinism checks green and the golden suite passing under stage1 and stage2 alike. A self-hosting compiler has no outside authority to check it against, so these two habits, the ratchet and the ladder, are how it stays honest about itself.
Future work
Section titled “Future work”Only the work the sources state is listed here.
After the bootstrap
Section titled “After the bootstrap”The bootstrap is done and the surface, reopened after 1.0.0, has been actively growing. 1.1.0 through 1.11.0 each shipped real language and library surface, from char as text and the daily driver operators, through the foreign boundary in both directions, to the numeric cast, the hash builtin, the generic map, and the string toolkit, then on to block and doc comments, the wasm cross-emit target, the machine readable diagnostics and doc model that editor tooling reads, the owning take that lets a program reclaim a heap tree, and the standard library growth that spent it, and most recently the owning func marker, a sound scoped frame in code generation, the finished match expression, and the for loop over the two standard containers. The recent lines lean toward the library and the tooling around it, which is where the sources point next. Growth continues by proposal rather than on a fixed schedule, alongside the three constants that never paused, sharper diagnostics, standard library growth, and soundness fixes. The concrete work the sources still name is the package tool and the standard library below.
Dawn: versioning and repeatable builds
Section titled “Dawn: versioning and repeatable builds”The dawn package tool is a minimal working seed today: an import resolves against the latest clone in the cache, and there is no version selection, no lock file, no fetch past the root file’s direct imports, and no integrity check. Its stated roadmap, in order:
- Version selection: pin a git tag or commit per package, chosen by a minimal version selection rule like Go’s and recorded so builds repeat.
- A lock file: a checked in manifest at the project root listing every package and its resolved version, so a fresh machine builds the same bytes.
- Graph fetch: walk the imports of fetched packages, not the root file alone, and resolve the whole graph before a build.
- Integrity: a hash per fetched module, verified on use, to catch a moved or rewritten tag.
- Quality of life: a vendor mode that copies dependencies into the tree, offline builds from the cache, and private repositories with authentication.
Standard library plans
Section titled “Standard library plans”The repository sketches the shape the standard library grows into. This is a direction, not a schedule. Much of the original sketch has since landed: a string keyed std.map in 0.1.3, made generic over its key as Map<K, V> in 1.5.2, mutable strings in 0.2.0, a conservative collector<T> heap in 0.5.1, Unicode aware string operations in 0.5.2, and Result<T, E>, a lazy IO<T>, and leveled std.logging in 0.5.3. The 1.4 line added std.fs, std.time, std.process, and std.json on top of the foreign boundary, and the 1.8 line added std.flags and std.set beside the map. What the sources still leave open:
- A precise collector to replace the conservative one, which the spec names as much later work.
- A
List<T>monad; the 0.5.3 helper additions acrossMaybe,Either, andResultfilled in most of the rest.
Following along
Section titled “Following along”The full history, newest first, is in the changelog, and the source lives at github.com/choice404/dusk.