Repository navigation
Releases: joagre/ernest
Release list
Ernest 0.9.0
Ernest 0.9.0 is the release of the first program built against an installed Ernest by a repository of its own: ernest-httpd, which serves ernest-lang.org over TLS since 2026-11-02, and what it asked of Ernest on its way, TLS in Tcp, a program under a pseudo-terminal in Os, four libraries, base64, crypto, time and http, and seventeen lines of feedback decided the days they came. With it come what the release review before it decided, a constructor shown with its module's name, a read given no time that has run out as it begins, and the report's sentences for every silence its readers found.
A program written for 0.8.0 changes these things. Its modules are built again, since ern run, ern test and Code.load refuse a compiled form another build of ern wrote. A node of 0.9.0 and one of 0.8.0 do not connect, as two minor releases never do. Io.show writes a constructor outside the prelude with its module's name, Io.Timeout for what was Timeout, so a program that reads show's text, or a test that compares against it, sees the name. Tcp.read and Os.read given no time answer Left(Timeout) whatever has arrived, as every wait does; a poll gives them a millisecond.
Json.parse's UnexpectedByte and UnexpectedSequence carry where they begin, at = Position(line, column), and the sequence is a String. Http.render is Http.renderResponse, Time.civil is Time.toCivil, and Time.weekday takes a Civil. A module named ernest.ern, or under ernest/, is refused, the namespace being the toolchain's own. A clause whose guard is the literal false is refused as a redundant clause.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The report of this release is what holds. What changed since 0.8.0 is the report's diff, git diff v0.8.0 v0.9.0 -- report/, and why is the decision log's entries after The Release Review Before 0.8.0 in docs/log.md.
What changed:
The language and the report
Ernesta taken namespace, the shell standing under it asErnest.Shell: §4.2, §11.1, §12.7; the log's Four Lines From the Deployment.- A clause whose guard is the literal
falseredundant: §5.9; The Release Review Before 0.8.0. - The caller of
Os.exitneither told nor waited for, a termination once the end has begun cutting the wait short, and a program under a terminal counting as a computation: §8.6; A Program After Its Exit, The Release Review Before 0.9.0. - A value reclaimed by its process's collection, in the host's two generations: §10; Four Lines From the Deployment, The Release Review Before 0.9.0.
- An archive's version text naming its commit: §11.7; An Archive Says Its Commit.
- A listener's family, and a node's: §12.5, Appendix E.18; Four Lines From the Deployment.
- The toolchain's own units listed, and an attach refused by a node that could not load them: §11.2, §12.7; Four Lines From the Deployment, The Release Review Before 0.9.0.
The toolchain and the shell
- An IPv4-mapped
listenrefused byern configand the node: §12.5; The Release Review Before 0.9.0. ernest.pidmade for its owner alone, andern stopandern reloadchecking the directory as the node does: §12.5; The Release Review Before 0.9.0.- The shell's modules under
src/shell/ernest/,Ernest.Shell: §11.2; Four Lines From the Deployment. - The terminal tests' harness an Ernest program over
Os.startTerminal: The Harness in Ernest.
The library
Tcp.listenTlsandTcp.connectTls, one socket over TLS or not,Verified(caFile)andUnverified: Appendix E.18; TLS inTcpand a Terminal inOs, Two Lines on TLS.Os.startTerminal,Os.resize,closeInputas a hangup, a program's process living on after its exit,Os.user,Os.exit's caller: Appendix E.23; TLS inTcpand a Terminal inOs, A Program After Its Exit, The Hangup's Order.Tcp.writeas the host's send, the endpoints of a closed connection, a read or an accept whose caller ended: Appendix E.18; A Close, an Exit and a Write, Three Flakes Read.libs/base64,libs/crypto,libs/timeandlibs/http: Appendix G.8 to G.11; The Four Libraries.Json's errors carrying aPosition, a broken literal or escape named at the byte that breaks it, and a too-long number found in one pass: Appendix G.6; The Error's Place, The Release Review Before 0.9.0.Io.showwriting a constructor outside the prelude with its module's name: Appendix E.1; The Release Review Before 0.9.0.- A read given no time having run out as it begins, in
TcpandOsas everywhere: Appendix E.0 shape rule 8, E.18; The Release Review Before 0.9.0. Fs.setMode, a path through a file, a loop of links and a long name: Appendix E.17; A Path Through a File.Io.writeError,Terminal.width,Supervisor.child,Random.nextFloat's bounds: Appendix E.1, E.19, E.21, E.12; Three Lines From the Server.Path.joinwriting its segments as given: Appendix E.14; A Path Through a File.Http's response readers reading a fold and a body to the close as RFC 9112 has a client read them, its renderers faulting on a value that would break the message, a status of three digits, one empty line before a request skipped: Appendix G.11; The Release Review Before 0.9.0.
The runtime
- A program's process group killed when the program is reaped, a pinned certificate taken sent alone or at a chain's end, and a CA file read before the dial: Appendix E.23, E.18; The Release Review Before 0.9.0.
- A fault's trace keeping its callers' frames, which the host's type pass had cut: §7.3, Appendix E.21; The Release Review Before 0.9.0.
ernest.pidmade for its owner alone, and a termination once the end has begun cutting the wait short: §12.5, §8.6; The Release Review Before 0.9.0.resizeansweringClosedafter the exit or the hangup: Appendix E.23; The Release Review Before 0.9.0.- A load's evaluator counting a running program and an open connection only: Appendix E.28; The Release Review Before 0.9.0.
What the review did not run: the service manager's checks under launchd, since no Mac was at hand; on macOS the archive is expected to work and is not yet verified.
The newcomer's program is pindex, a parallel inverted-index builder in four modules of 409 lines: a crawler runs a monitored worker for each file, at most a given number at once, an index process keeps each word's files and answers lookups by a call, the index is written to a file and renamed over it, and the command line answers a word a line from standard input with the files it is in; its tests cover the tokenizer, the index, a killed index and a crawl with an unreadable and a broken file. It built at the first try, its nine tests passed, and it ran; it is published beside the archive.
Ernest 0.8.0
Ernest 0.8.0 is the release of the pun: a field given or matched by the variable of its own name is written once, Desk(=local). With it come what its day decided, a receive that always receives with Clock.sleep as the one wait for a time, an alarm that can be canceled, a receive guard that reads a field, a doc example run at its own mailbox type, and what the release review before it decided. Its row in the plan is https://github.com/joagre/ernest/blob/v0.8.0/docs/plan.md, and what Ernest is and why is https://github.com/joagre/ernest/blob/v0.8.0/guide/why.md.
A program written for 0.7.0 changes these things. Its modules are built again, since ern run, ern test and Code.load refuse a compiled form another build of ern wrote. A node of 0.8.0 and one of 0.7.0 do not connect, as two minor releases never do. A receive with an after clause alone is refused and is written Clock.sleep(ms); a function that waited so receives nothing now, and a receiving e written on it is refused. Clock.alarm and Clock.alarmAt answer an Alarm, so a site that drops it writes let _ =. A request whose only field is its reply is declared positional and passed to Address.call bare, which the style asks for and the style test holds, as it holds the pun.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The report of this release is what holds. What changed since 0.7.0 is the report's diff, git diff v0.7.0 v0.8.0 -- report/, and why is the decision log's entries after The Release Review Before 0.7.0 in docs/log.md.
What changed:
The language and the report
- The pun,
=fforf = f, in a construction, an update, a fill and a pattern: §5.6, §5.10, Appendix A; the log's The Pun. - Principle 2's sentence admitting a field paired with the name that gives or binds it: §0; The Pun, The Release Review Before 0.8.0.
- A
receivealways receives,Clock.sleepthe one wait for a time, aNeverbody sleeping: §3.9, §4.6, §6.3, §6.8, Appendix A; AreceiveAlways Receives. - A guard reading a field selected from a variable in scope or a top-level
let: §6.3; The Pun's Opening Round. Alarmbound to its node, with the runtime's equality, a resource: §3.10, §3.11; An Alarm Can Be Canceled.- A restart and a foreign fault reaching a sleep and a load's wait: §6.9, §8.4; The Release Review Before 0.8.0.
- A wait in a function the runtime applies being the applying process's own: §6.5, §6.9; The Release Review Before 0.8.0.
- A sleep's cost growing with the messages waiting: §10; The Release Review Before 0.8.0.
- What a load's undoing kills having ended before the load answers: Appendix E.28;
make version, Appendix D Whole, the Settler's Restart. - Appendix D written whole, with
made,ownedandcontains: Appendix D;make version, Appendix D Whole, the Settler's Restart.
The toolchain and the shell
- A doc example running at the mailbox type of its function, and shape rule 6's one test for one that stays compiled: §11.2, Appendix E.0; Ernest 0.8.0, a Small Release.
- The help for a
receivewith anafterclause alone namingClock.sleep: §6.3, §11.5; AreceiveAlways Receives. - The shell's line at a binding's end naming the site alone: §11.2; The Pun's Opening Round.
- The style test refusing
f = fand a one-field request with a named reply:guide/style.md; The Pun, Ernest 0.8.0, a Small Release. make version V=x.y.z, withVERSIONnaming the release the checkout works toward:docs/release_review.md;make version, Appendix D Whole, the Settler's Restart.
The library
Clock.sleep: Appendix E.15; AreceiveAlways Receives.Clock.AlarmandClock.cancel,alarmandalarmAtanswering anAlarm: Appendix E.15; An Alarm Can Be Canceled.Utilization.schedulersreceiving nothing: Appendix G.4; The Pun's Opening Round.Receivingcovering a process inClock.sleep: Appendix E.21; The Release Review Before 0.8.0.- Nine examples of the library showing their values, the clock's among them: Appendix E.0; Ernest 0.8.0, a Small Release.
The runtime
Clock.sleepholding the timed mark, a restart asked for and a fault in the mailbox reaching it: §6.9, Appendix E.15; AreceiveAlways Receives.- An alarm canceled by any process of its node, and ending with its setter's restart: Appendix E.15; An Alarm Can Be Canceled.
What the review did not run: the service manager's checks under launchd, since no Mac was at hand; on macOS the archive is expected to work and is not yet verified. make sanitize was not run, the helper being unchanged since 0.7.0.
The newcomer's program is scheduler, a job scheduler in two modules of 335 lines: a scheduler process runs at most capacity jobs at once, each in a monitored worker under a deadline by Clock.alarm, kills a worker that overruns and retries a failed job up to its attempts, keeping a caller's Reply until the job ends; the command line takes jobs as name=sleep/ms|sum/n|fail/text. Beside the archive as scheduler.ern and jobs.ern: ern build --build-root build . in their directory, then ern test build/scheduler.erc, then ern run build/jobs.erc nap=sleep/300 add=sum/100 slow=sleep/2000 bad=fail/boom quick=sleep/100.
Ernest 0.7.0
Ernest 0.7.0 is the release of a shell attached to a running node: ern shell --config-dir dir --attach name runs each input on a listed peer, lists its processes and loads and reloads its modules, while the operator's machine keeps the terminal and the files. It also brings the report pared to the language, with the runtime's mechanism a chapter of its own, and what the release review before it decided. Its row in the plan is https://github.com/joagre/ernest/blob/v0.7.0/docs/plan.md, and what Ernest is and why is https://github.com/joagre/ernest/blob/v0.7.0/guide/why.md.
A program written for 0.6.0 changes these things. Its modules are built again, since ern run, ern test and Code.load refuse a compiled form another build of ern wrote. A node of 0.7.0 and one of 0.6.0 do not connect, as two minor releases never do; a configuration directory 0.6.0 made serves as it is, but ern config now writes a listen only with --listen, so a directory that peers must reach is made with it.
ern test exits with status 1 where a process faulted during the run, a service binding starting under it as under ern run. A node whose carrier cannot start ends as a job that cannot start, with status 1 and no fault. In a shell, Os.exit faults a process an input started, a site carries its session's namespace, let p <- e is taken at the prompt, and :quit leaves an attached shell with its node running on. restarting called in a function the runtime applies faults, and a foreign function typed by its callback receives through it alone. Four library functions are new, each marked since 0.7.0.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The report of this release is what holds. What changed since 0.6.0 is the report's diff, git diff v0.6.0 v0.7.0 -- report/, and why is the decision log's entries after The Release Review Before 0.6.0 in docs/log.md.
What changed:
The language and the report
- The runtime's mechanism in chapter 12, §10 in
toolchain.md: §10, §12; the log's The Report Pared, Built. - The
Peerfunctions' contracts under E.27: Appendix E.27; The Second Cut, §8.7 to E.27. - §12.1's keys and §12.3's causes binding every runtime: §12; The Argument's Four Decisions.
- A restart in a function the runtime applies refused: §6.9, §7.4; The Argument's Four Decisions.
- A foreign function typed by its callback receiving through it alone: §4.7; The Argument's Four Decisions.
- A deadlock while a load's evaluator waits faulting the evaluator: §8.6; The Argument's Four Decisions.
- A session's namespace carrying its number, and on a node the node's key: Appendix H; The Attached Shell's Sentences, Sites, Sessions and Their Names.
- An address the permission to send, a
ProcesswhatProcess.killends: §6.5; Reload, Services and Tests. - The report's paragraphs, none past 150 words: the log's The Report Pared, Built.
The toolchain and the shell
- The attached shell,
ern shell --attach name: §11.2 The attached shell, §11.7, §11.8; The Attached Shell's Cut, The Attached Shell's Sentences, The Attached Shell, Built, The Attached Shell, Tested. - The attach waiting twice a dial's time: §11.2; The Shell's Own Node.
- Sessions numbered, a site carrying its session's namespace: §11.2 Faults, Startup and history; Sites, Sessions and Their Names.
- A reload said at every session's prompt: §11.2 The attached shell; Sites, Sessions and Their Names.
:quitleaving every shell,:detachnone: §11.2 Commands; One Word to Leave.:processeswith each process's activity: §11.2 Commands; The Shell's Second Round, Decided Again.- A binding input's process living while its name is in scope, and what it owned said at its end: §11.2 Inputs; The Shell's Second Round, Decided Again, The Input's Process.
let p <- eat the prompt, anditafter aLeft: §11.2 Inputs;<-at the Prompt, The Input's Process.- A prompt's binding read on the session's node: §11.2 Inputs; The Input's Process.
- An expression's input one unit, an input's cost flat in the session: §11.2 Inputs; A Line-Mode Input's Cost, An Expression's One Unit, and an Input's Cost Flat.
- A load evaluating in an evaluator that ends with its work: §11.2 Loading and reloading; The Argument's Four Decisions, Sites, Sessions and Their Names.
- A compiled form taken by
:loadjudged by its source's digest: §11.2 Loading and reloading; The Release Review's Code Fixes. :docdocumenting the code the node loaded: §11.2 Commands; The Claims, the README and the Tour.Os.exitby session, and from the shell's own code ending the shell: §11.2, Appendix E.23; The Attached Shell's Sentences, The Library's Lines.ern configwithout--listenwriting nolisten: §11.3; The Shell's Own Node.- A carrier that cannot start refusing the job, an end of the program with its subscribers told: §12.5, §8.6, §11.8; The Shell's Own Node, The Release Review Before 0.7.0.
- The protocol's version moving at a release whose frames differ: §12.5; The Release Review Before 0.7.0.
ern testred where a process faulted, and a service binding started under it: §11.2, §11.8; Reload, Services and Tests.ern runandern testrefusing a compiled form another build wrote: §11.2; One Function per Initializer.- A private function or binding refused as private: §11.5; The Claims, the README and the Tour.
- A field read in a
receiveguard answered with a help naming theletbefore thereceive: §6.3; The Release Review's Code Fixes. - A block's
letwhose initializer names its own name reported as a cycle: §4.6. - A standard library shim called as the host's function at the call site: §11.1; The Emitter's Calls Against the Host's, Built.
- Each initializer a function of its own, and the build's cost linear in its functions: §11.1; The Build's Cost per Function, One Function per Initializer.
The library
Process.kill: Appendix E.21; Reload, Services and Tests.Process.ownedandEts.owned: Appendix E.21, G.1; The Input's Process.Test.until, yielding between its runs: Appendix E.24, E.0 shape rule 8; What the Newcomer's Feedback Left,Test.until, Built, The Library's Lines.Code.loadevaluating in an evaluator, the load undone where its caller ends while it waits: Appendix E.28; The Argument's Four Decisions, Sites, Sessions and Their Names, The Release Review Before 0.7.0.Process.liveandProcess.faultsleaving sessions' own processes, evaluators and a process that has ended out: Appendix E.21; The Session's Screen and the Toolchain's Units, Built, Sites, Sessions and Their Names.- A shim's raise traced from the caller's line: Appendix E.21; The Emitter's Calls Against the Host's, Built, The Release Review's Code Fixes.
Peer.computenever drawing an attached shell's own node: Appendix E.27; The Attached Shell's Own Node, Quiet.
The runtime
- A session's screen, a write going to its writer's session: §12.1; The Session's Screen and the Toolchain's Units, Built.
- The toolchain's own units loaded from the node's tree, the frame saying whose a unit is: §12.6, §12.7; The Session's Screen and the Toolchain's Units, Built, The Shell's Own Node.
- An attached shell's own node telling no utilization and saying nothing of its session's node: §12.5, E.27; The Attached Shell's Own Node, Quiet.
- A detached session's units that declare a function kept for the node's life: §11.2 What a node holds; The Detach Keeps What Declares a Function.
- An attached shell's last lines on rows of their own, and a shell's end losing none: §11.2; The Attached Shell's Last Row, The End's Lines Reach the Screen.
What the review did not run: the service manager's checks under launchd, since no Mac was at hand; on macOS the archive is expected to work and is not yet verified.
The newcomer's program is auction, an auction house whose house process opens lots and takes bids by a call, and sells each lot to its highest bidder at its deadline, writing the sale to a ledger; one module beside the archive: ern build auction.ern && ern test auction.erc, then ern run auction.erc demo, where three robots bid for two lots. Run as a node with --config-dir, the house takes lots and bids from an attached shell's prompt, Auction.open("a lamp", 10, 60), its ledger sales.log in the node's working directory.
Ernest 0.6.0
Ernest 0.6.0 is the release after what the review before 0.5.0 and the release itself left was done: a
service monitor on a process of another node is now told of its restarts too, by the runtime's own
frames, so that monitorService means one thing wherever the process lives; the test harness counts
each test's steps; and a release review read what changed. Its row in the plan is
https://github.com/joagre/ernest/blob/v0.6.0/docs/plan.md, and what Ernest is and why is
https://github.com/joagre/ernest/blob/v0.6.0/guide/why.md.
A program written for 0.5.0 changes these things. A service monitor on a process of another node
is told Restarted at each of its restarts, where 0.5.0 told it nothing, and a restart of a process
that a process of another node monitors or calls waits for that node's answer, at the latest until a
loss. A node of 0.6.0 and one of 0.5.0 do not connect, since the protocol's version moved. ern format lays a lambda whose body does not fit on its line out with the body on the next line, so a
source formatted by 0.5.0 may move. A diagnostic's shown source line carries no trailing blanks.
FaultReport is unchanged.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The
report of this release is what holds. What changed since 0.5.0 is the report's diff,
git diff v0.5.0 v0.6.0 -- report/, and why, the decision log's entries after The Release
Review Before 0.5.0 in docs/log.md.
What the review did not run: the service manager's checks under launchd, since no Mac was at
hand; on macOS the archive is expected to work and is not yet verified.
The newcomer's program is lending, a lending library whose desk process lends the copies of a
catalog read from a file, queues patrons and tells them as a copy comes back, and takes back what
a patron held when the patron's process faults; one module beside the archive with its catalog:
ern build lending.ern && ern test lending.erc, then ern run lending.erc catalog.txt ledger.txt.
Ernest 0.5.0
Ernest 0.5.0 is the release after the whole of Ernest was read once more, the report, the
guide, the code and the tools, each by a reader of its own, and what they found was fixed or
decided. It adds the service monitor, a monitor that is also told each time a process restarts;
compare, the one function form of every ordering, over tuples, lists, Optional and Either
too; the tour, one program grown through eight chapters; and the design of the runtime, the
nodes and the code written down beside the shell's. Its row in the plan is
https://github.com/joagre/ernest/blob/v0.5.0/docs/plan.md, and what Ernest is and why is
https://github.com/joagre/ernest/blob/v0.5.0/guide/why.md.
A program written for 0.4.0 changes these things. A member has one face: it is named in a
declaration and in a needs, and used through its operator or compare, so Int.compare(a, b)
is compare(a, b), Int.negate(x) is -x, Bool.not(b) is !b, and Prelude.List.size is
List.size. List.sort(list, f) is List.sort(list), whose elements have a compare, or
List.sortBy(list, key). A match on a Reason that lists every constructor gains Restarted,
the reason a service monitor delivers. A supervisor's mailbox type is Supervisor.GroupMsg, where it
was Supervisor.Msg. A FaultReport has three more fields, callee, peer and time, so a
pattern that named its fields by position changes. A receive with a pattern clause in a
function whose mailbox nothing fixes, spawn(fn() = receive { x -> Unit }), is refused, as the
report always said, since no message can reach it. ern build, ern doc and ern format no
longer take --short-errors. A node refuses a configuration directory, its ernest.conf or its
certificate.pem that its group may write, as it refused one anyone may write.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The
report of this release is what holds. What changed since 0.4.0 is the report's diff,
git diff v0.4.0 v0.5.0 -- report/, and why, the decision log's entries after The Release
Review Before 0.4.0 in docs/log.md.
What the review did not run: the service manager's checks under launchd, since no Mac was at
hand; on macOS the archive is expected to work and is not yet verified.
The newcomer's program is kvd, a key-value server over TCP that keeps its values in a log file, four modules beside the archive with the script that drives it: ern build --build-root build kvd && ern test build, then ern run build/kvd/server.erc 7072 data.log in one terminal and a script of commands piped into ern run build/kvd/client.erc 7072 in another, as demo.sh does.
Ernest 0.4.0
Ernest 0.4.0 is the first release with nodes. A program runs on several nodes, each started
from a configuration directory that lists its peers by name, public key and address, and
talking to them over TLS. A process spawned on a peer brings its code along, every definition
known by the hash of its content, so a node with no program of its own takes part, and a
computation that names no node goes to one with room. A service is found by name across
nodes. The shell reloads a module as a new version beside the old, and Code loads a compiled
module into a running node. The release is MVP 3.2's row in the plan,
https://github.com/joagre/ernest/blob/v0.4.0/docs/implementation_plan.md, and what Ernest is
and why is https://github.com/joagre/ernest/blob/v0.4.0/guide/why.md.
A program written for 0.3.1 changes two things. A function that receives writes receiving M
where it wrote with M, and a function that only sends writes nothing. The ! on a function
type is gone, and so is the obligation on a reply it marked: a reply is a value, answered once.
What the shell does not yet do is attach to a running node with its inputs run there; an
operator reaches a node today as the deployment guide's section 6 says,
https://github.com/joagre/ernest/blob/v0.4.0/guide/deployment.md.
Until 1.0 a release may refuse a program the previous release accepted, as the plan says. The
report of this release is what holds. What changed since 0.3.1 is the report's diff,
git diff v0.3.1 v0.4.0 -- report/, and why, the decision log's entries after The Release
Review Before 0.3.1 in docs/decisions.md.
What the review did not run: the code's reader read the node's modules alone, the carrier, the
gateway, the peer, the exchange and the arrival, and the rest of the code waits for the full
review before 0.5.0; and the service manager's checks under launchd, since no Mac was at hand;
on macOS the archive is expected to work and is not yet verified.
The newcomer's program is pubsub, a broker of topics driven by a script on standard input,
beside the archive with the two scripts it was tried on. On one node:
ern build pubsub.ern && ern run pubsub.erc < script.txt. On two, a bare node foo and the
program's node, each made with ern config and listing the other as the deployment guide's
section 1 says: ern run --config-dir foo on one terminal, and
ern run --config-dir a pubsub.erc < script2.txt on the other.
Ernest 0.3.1
Ernest 0.3.1 is a documentation release, from MVP 2.99d: the manual pages of the standard
library, the prelude and the libraries rewritten to teach, the examples made to teach and
listed in an order to read them, the guide staged for its reader, and the report's contracts
stated more precisely, with no rule of the language changed.
Until 1.0 a release may refuse a program the previous release accepted. The report of this
release is what holds. What changed since 0.3.0 is the report's diff,
git diff v0.3.0 v0.3.1 -- report/, and why, the decision log's entries of 2026-10-05 after
The Release Review Before 0.3.0 in docs/decisions.md.
What the review did not run: its readers, since this release changes the documentation and
not the language; a cold reader, briefed as a newcomer, read the rewritten pages instead. And
the service manager's checks under launchd, since no Mac was at hand; on macOS the archive is
expected to work and is not yet verified.
No newcomer wrote a program for this release. taskrun, 0.3.0's, builds and runs under
0.3.1 as it did: ern build taskrun.ern && ern run taskrun.erc < build.tasks.
Ernest 0.3.0
Ernest 0.3.0 is the third release of Ernest, and the end of MVP 2.99c: the core language
argued sound and generated against, before peers build on it. Three machines run in every
make test: programs derived from the grammar of Appendix A, laws of the standard library
checked on cases drawn afresh, and well-typed programs held to an interpreter of the
report's rules. docs/soundness.md argues that a well-typed program does not go wrong. A
full review read every area, and its 570 findings were worked before the tag. The release
carries MVP 2.99b's work too: operations records, needs a.compare with a record filled
from a namespace and derives compare, OrderedSet and OrderedMap, and a program run
by the host's service manager.
Until 1.0 a release may refuse a program the previous release accepted. The report of this
release is what holds. What changed since 0.2.0 is the report's diff,
git diff v0.2.0 v0.3.0 -- report/, and why, the decision log's entries since 2026-10-01
in docs/decisions.md.
What the review did not run: the service manager's checks under launchd, since no Mac was
at hand; on macOS the archive is expected to work and is not yet verified.
The newcomer, who read the guide and wrote a program of their own, wrote taskrun, a task
runner: tasks read from standard input, each run in a monitored process once the tasks it
requires have finished, a few at a time, one that runs past its limit killed by an alarm,
and the tasks skipped and those caught in a cycle reported at the end. It is published
beside the archive with build.tasks, the tasks it was tried on:
ern build taskrun.ern && ern run taskrun.erc < build.tasks.
Ernest 0.2.0
Ernest 0.2.0 is the second release of Ernest, and the first after the principles review: the report and the guide
were read against the five principles of §0, and §0 against what it had decided, on
2026-09-30 and 2026-10-01. The review's product is sentences, not features: §0 gained a
paragraph on the host and one sentence for each principle, and ten sections one sentence
each, under which a family of exceptions became instances of a rule. Programs written for
0.1.0 change in the places below; each is in the report's revision of 1 October 2026, and
the log's entries of that day say why.
What changed for a program:
spawn(f)andspawnMonitored(f, wrap)take no placement;LocalandWhereare
gone. A spawn on a peer isPeer.spawn(name, f), a module of MVP 3.0, refused until then.via(addr, f)takes the address first.- A
Downnames its process:Down(process, reason, site). - Tests are
Test.Case(name, run)withTest.PassedandTest.Failed(text), of the
standard library's moduleTest(Appendix E.24);TestandTestResultleft the prelude. - The built-in type
Foreignis the library's foreign typeForeign.Term(E.12), with the
runtime's exact equality;Foreign.toBytesreads any binary. - A constructor's named fields are stored, sent and shown in the order they are declared.
Io.debugwrites to standard error;Io.showandIo.debugare resolved at a known type,
a type error on a type variable, and write a foreign value as<foreign>.- A printed type marks a process-only effect variable
m+, besidea=anda!. Terminal'sstyled, cursor moves and erasures are the libraryAnsi(G.3);
Terminal.sizeanswersEither.- A reply may stand in a list, an
Optionalor anEither; §6.6 names no type. - A member is an operator,
compareornegatealone;letdeclares no member. - Parentheses after
|>change nothing;1e10is a float; a tuple has two or more
components; a stray doc block is an error. Tcp.write,Os.readandOs.writetake milliseconds;Os.starttakes none;
Os.environment(name)asks by name;Os.giveandTcp.givepass a resource on; a
listener has an owner.Io.Errornames its causes,NotAFile,Exists,NotUtf8,Invalid;Otherholds the
host's words.Fs.makeFile,Fs.setModeandFs.makeHardLink;Path.<>joins two paths and
Path.jointakes the segments;Path.nameisNonefor the root;
Path.withoutExtension;List.dropLast(xs, n);String.words;Float.pi;
Io.writeError;Map.mergeWith's function takes the key;List.<>is Ernest.- Gone:
Optional.orElse,Either.orElse,Char.isAsciiDigit,Fs.create,
Fs.makePrivate. - The float
+and-are no longer normalized for negative zero, which they cannot make.
The newcomer, who read the guide and wrote a program of their own, wrote a key-value cache whose
entries expire after a time or when the process that leased them ends; it is published beside
the archive, cache.ern, demo.ern and checks.ern.
Ernest 0.1.0
Ernest 0.1.0 is the first release of Ernest. It is a functional language for concurrent programs, running on the Erlang runtime. A program is made of pure functions, whose types the compiler infers, and of processes that talk through typed mailboxes. This release runs programs on one node. Programs across nodes, and code known by its hash, come in later releases.
What is in it
- The language on one node, as the report defines it:
- a function's mailbox type is part of its type, and every
sendis checked against its receiver; - a
Replyis checked to be answered exactly once on every path; Address.callwaits with a deadline;restartingandSupervisorrestart processes;- typed boundaries check what crosses to and from Erlang code;
- bitstrings;
- and a foreign function interface for what only the host can do.
- a function's mailbox type is part of its type, and every
- One command,
ern:buildcompiles;runruns;testruns a module's tests;shellis an interactive shell with line editing, completion and documentation;docwrites a module's page as CommonMark or a manual page;formatlays out a source;configmakes a node's configuration.
- A standard library of 23 modules (Appendix E of the report), among them
List,Map,Set,String,Bytes,Path,Io,Fs,Tcp,Clock,Terminal,Os,ProcessandSupervisor. Two libraries outside it:libs/etsandlibs/markdown. - Manual pages:
man ern, andman Ernest.Listand the like for each module. - An Emacs mode.
- The guide, Programming in Ernest, and example programs in
examples/: a game at a terminal, a small web server, file synchronization, and a read-eval-print loop.
This is the first release, so there is no earlier one to compare it with.
What it does not do yet
-
Peers.
spawn(Peer(...))faults withpeer unreachable, and a node'sernest.confis not read. Distributed code and the node protocol are the next release's (the plan's MVP 3.0). -
Known limits, stated in the report:
- Through a type variable,
Io.showwrites a value by its runtime representation, an abstract type's included (§4.4). - A foreign function's result and a callback's arguments at a type variable are not checked at the boundary (§8.4).
- A value made with
Foreign.fromcrosses into foreign code unchecked (§8.4).
All three close in the next milestone, MVP 2.99b.
- Through a type variable,
Installing
The archive ern-0.1.0.tar.gz below needs Erlang/OTP 29 on your PATH, make, and a C compiler. The runtime has one helper in C: it runs the programs Os.start and Os.run start, and removes a tree for Fs.removeAll. In the directory the archive unpacks to:
make
sudo make install # into /usr/local; `make install PREFIX=$HOME/.local` for you alone
ern --version
man ern
It is built and tested on Linux. macOS is expected to work the same way and is not yet verified.
A newcomer's program
Before the release, a programmer who knew other languages but not Ernest read the README and the guide alone, then wrote a program: findex, attached below. It indexes the words of every file under a directory with a pool of workers, one process per file, each under a deadline. It then answers queries, a word per line of standard input, with the files that hold the word and how often.
$ ern build findex.ern
$ printf 'cat\n' | ern run findex.erc data 4
cannot list data/closed: Denied
not text, skipped: data/blob.bin
Findex.startWorkers:222 faulted: cannot read: Denied
failed: data/locked.txt: cannot read: Denied
indexed 4 files, 21 words; 1 not text, 1 failed, 0 overdue
cat:
2 data/cat.txt
1 data/pets.txt
Where the language made the program harder to write went into the release review, and from there into the report, the guide, and the plan.