Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ $ node tools/examples.mjs
That last step rewrites `examples/index.json`, which is the list the picker
reads. Everything in it comes from somewhere else: each summary is the comment
at the top of the file, and whether an example has a `main` to run is what the
pinned artifact answered when asked. Seven of the twenty-nine do; the picker
turns Run off for the other twenty-two and says why, rather than letting
pinned artifact answered when asked. Seven of the twenty-eight do; the picker
turns Run off for the other twenty-one and says why, rather than letting
somebody press it and be refused.

Forgetting that step is caught rather than shipped. `tools/check.mjs` asks the
Expand All @@ -98,7 +98,7 @@ which is also what stops a generated file being edited by hand: it looks
exactly like a generated file.

`greeting.deed` is left out because it imports two other modules and this page
hands the compiler one file. That is the only one: the other twenty-nine were
hands the compiler one file. That is the only one: the other twenty-eight were
checked through the pinned artifact and every one of them is clean.

## Layout
Expand Down Expand Up @@ -129,7 +129,7 @@ the release the pin names, and the example index still says what the compiler
says.

That last part is not a shape check. It loads the pinned artifact, asks it its
version, and asks it about all twenty-nine examples, because a committed wasm
version, and asks it about all twenty-eight examples, because a committed wasm
is a file nothing here ever built: a truncated copy, or a different build
wearing the right name, spells its filename correctly and passes everything
else. It runs on every pull request too.
Expand Down
Binary file removed assets/deed-v0.2.2-wasm32-unknown-unknown.wasm
Binary file not shown.
Binary file added assets/deed-v0.2.3-wasm32-unknown-unknown.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions assets/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// artifact carries all of them, so this page cannot document a code the
// compiler does not have, or miss one it does.

const TAG = "v0.2.2";
const VERSION = "0.2.2";
const TAG = "v0.2.3";
const VERSION = "0.2.3";
const WASM_URL = `../assets/deed-${TAG}-wasm32-unknown-unknown.wasm`;

const STATUS = document.getElementById("status");
Expand Down
4 changes: 2 additions & 2 deletions assets/play.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
// release, because a release asset cannot be fetched from a browser at all:
// both the download URL and the API one redirect to a host that sends no
// `Access-Control-Allow-Origin`. See decisions/2026-07-31-no-build-step.md.
const TAG = "v0.2.2";
const VERSION = "0.2.2";
const TAG = "v0.2.3";
const VERSION = "0.2.3";
const WASM_URL = `../assets/deed-${TAG}-wasm32-unknown-unknown.wasm`;

const SOURCE = document.getElementById("source");
Expand Down
60 changes: 0 additions & 60 deletions examples/calendar.deed

This file was deleted.

10 changes: 2 additions & 8 deletions examples/index.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
{
"tag": "v0.2.2",
"tag": "v0.2.3",
"examples": [
{
"file": "calculator.deed",
"summary": "A small expression language, written in Deed: split into tokens, parse by precedence, evaluate. Tokens are space separated on purpose, to keep character-by-character lexing out of the way of the part this file is actually about, which is precedence and associativity.",
"runs": false,
"tests": 3
},
{
"file": "calendar.deed",
"summary": "A calendar on top of `Io.epoch`, which only gives milliseconds since 1970. design/04-capabilities.md says the language has no way to write one yet: this is that attempt, to find out whether that is still true.",
"runs": false,
"tests": 3
},
{
"file": "closures.deed",
"summary": "Closures, the two holes they used to open, and the boundary they can now cross.",
Expand Down Expand Up @@ -89,7 +83,7 @@
"file": "logs.deed",
"summary": "A program that reads a directory of logs and reports what is in them.",
"runs": true,
"tests": 11
"tests": 13
},
{
"file": "markdown.deed",
Expand Down
46 changes: 41 additions & 5 deletions examples/logs.deed
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@

module examples/logs

use std/ratio.{percent_or, percent_text}

use std/table.{Entry, Table, or_else, set}

// What this program counts: a key, and how many times it turned up. Naming it
Expand Down Expand Up @@ -151,23 +153,40 @@ fn padded(text: String, width: Int) -> String {
text + join(repeat(" ", width - length(text)), "")
}

fn line_for(one: Entry<String, Int>) -> String {
padded(one.key, 16) + to_string(one.value)
// One row: the key, how many, and what share of the whole that is.
//
// The share is the thing this file could not write when it was first written.
// `design/fractional-values.md` refused every fractional number type and said
// the first real need would be a report wanting exact ratios that become text
// at the edge. This is that call, and it is answered by `std/ratio` rather than
// by the language: the percentage is an exact ratio right up until
// `percent_text` renders it, and the rounding is a decision this file can see.
fn line_for(one: Entry<String, Int>, total: Int) -> String {
let share = percent_or(percent_text(one.value, total, 1), "-")
padded(one.key, 16) + padded(to_string(one.value), 8) + share
}

fn total_of(counts: Counts) -> Int {
for one in counts with sum = 0 {
sum + one.value
}
}

// The whole report, as lines. Pure, so a test can read it.
fn report(lines: List<String>) -> List<String> {
let levels = ranked(tallied(lines, level_of))
let sources = ranked(tallied(lines, source_of))
let level_total = total_of(levels)
let source_total = total_of(sources)

let head = ["by level", padded("", 16) + "count"]
let head = ["by level", padded("", 16) + padded("count", 8) + "share"]
let with_levels = for one in levels with out = head {
push(out, line_for(one))
push(out, line_for(one, level_total))
}
let with_gap = push(with_levels, "")
let with_head = push(with_gap, "by source")
for one in sources with out = with_head {
push(out, line_for(one))
push(out, line_for(one, source_total))
}
}

Expand Down Expand Up @@ -275,6 +294,23 @@ test "an empty directory still reports its headings" {
assert length(out) == 4
}

test "every row says what share of the whole it is" {
// Three lines, two of one level and one of the other, so the shares are
// `2/3` and `1/3`. Neither has a finite decimal expansion, which is the
// whole reason this column could not be written before `std/ratio`: the
// value stays exact and only the last step rounds.
let lines = ["d INFO ledger x", "d INFO ledger y", "d ERROR audit z"]
let out = report(lines)

assert at(out, 2) == ok(padded("INFO", 16) + padded("2", 8) + "66.7")
assert at(out, 3) == ok(padded("ERROR", 16) + padded("1", 8) + "33.3")
}

test "a share of a single line is the whole of it" {
let out = report(["d WARN cache x"])
assert at(out, 2) == ok(padded("WARN", 16) + padded("1", 8) + "100.0")
}

test "appending walks one list onto the other" {
assert appended(["a"], ["b", "c"]) == ["a", "b", "c"]
assert appended([], []) == []
Expand Down
19 changes: 19 additions & 0 deletions examples/scheduler.deed
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@
// - a standalone recursive function to drain the queue (ordinary `Diverge`)
//
// Nothing about the scheduler is language machinery.
//
// It is also not a library, and the reason is worth being exact about, because
// "concurrency is a library here" is the claim this file is usually read as
// making. What it shows is that a scheduler can be *written* in Deed. It cannot
// be *shipped*, and a program that wants one has to copy this file and edit it.
//
// The wall is one line above: `Schedule.fork` takes a
// `Fn() uses Schedule.fork, Schedule.yield, Log.note -> ()`, and that row names
// `Log.note` because these particular tasks log. A library cannot know what its
// tasks will perform, so the queue element type would have to be
// `Fn() uses r -> ()` for a row variable `r` belonging to the effect. Effects do
// not take row parameters: `effect Schedule<uses r>` does not parse, and
// `DEED5008` separately confines a row variable to a function-typed parameter's
// row and the declaration's own `uses`, which a handler's `state` is neither.
//
// So the queue's element type has to name a concrete row, the row has to name
// every effect a task might perform, and both are things only the program
// knows. That is why `std/task` does not exist next to `std/list`, and it is a
// row-polymorphism gap rather than anything about concurrency.

module examples/scheduler

Expand Down
32 changes: 16 additions & 16 deletions install/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ <h1>Install</h1>
</p>

<!--
Every `deed 0.2.2` below is the output of that binary on that
Every `deed 0.2.3` below is the output of that binary on that
platform, printed by the release workflow, which refuses to publish
when a binary disagrees with its tag. The program output further down
was produced by running the released Windows build.
Expand All @@ -83,15 +83,15 @@ <h2>Take the file for your machine</h2>
<tbody>
<tr>
<td>Linux, 64-bit Intel or AMD</td>
<td><code>deed-v0.2.2-x86_64-unknown-linux-gnu.tar.gz</code></td>
<td><code>deed-v0.2.3-x86_64-unknown-linux-gnu.tar.gz</code></td>
</tr>
<tr>
<td>macOS, Apple silicon</td>
<td><code>deed-v0.2.2-aarch64-apple-darwin.tar.gz</code></td>
<td><code>deed-v0.2.3-aarch64-apple-darwin.tar.gz</code></td>
</tr>
<tr>
<td>Windows, 64-bit</td>
<td><code>deed-v0.2.2-x86_64-pc-windows-msvc.zip</code></td>
<td><code>deed-v0.2.3-x86_64-pc-windows-msvc.zip</code></td>
</tr>
</tbody>
</table>
Expand All @@ -110,23 +110,23 @@ <h2>Unpack it and ask it what it is</h2>
</p>

<h3>Linux</h3>
<pre class="code"><code>$ tar xzf deed-v0.2.2-x86_64-unknown-linux-gnu.tar.gz
$ ./deed-v0.2.2-x86_64-unknown-linux-gnu/deed --version
deed 0.2.2</code></pre>
<pre class="code"><code>$ tar xzf deed-v0.2.3-x86_64-unknown-linux-gnu.tar.gz
$ ./deed-v0.2.3-x86_64-unknown-linux-gnu/deed --version
deed 0.2.3</code></pre>

<h3>macOS</h3>
<pre class="code"><code>$ tar xzf deed-v0.2.2-aarch64-apple-darwin.tar.gz
$ xattr -d com.apple.quarantine deed-v0.2.2-aarch64-apple-darwin/deed
$ ./deed-v0.2.2-aarch64-apple-darwin/deed --version
deed 0.2.2</code></pre>
<pre class="code"><code>$ tar xzf deed-v0.2.3-aarch64-apple-darwin.tar.gz
$ xattr -d com.apple.quarantine deed-v0.2.3-aarch64-apple-darwin/deed
$ ./deed-v0.2.3-aarch64-apple-darwin/deed --version
deed 0.2.3</code></pre>
<p>
The middle line is not optional and it is explained below.
</p>

<h3>Windows</h3>
<pre class="code"><code>&gt; Expand-Archive deed-v0.2.2-x86_64-pc-windows-msvc.zip .
&gt; .\deed-v0.2.2-x86_64-pc-windows-msvc\deed.exe --version
deed 0.2.2</code></pre>
<pre class="code"><code>&gt; Expand-Archive deed-v0.2.3-x86_64-pc-windows-msvc.zip .
&gt; .\deed-v0.2.3-x86_64-pc-windows-msvc\deed.exe --version
deed 0.2.3</code></pre>

<h2>What your operating system will say about it</h2>
<p>
Expand Down Expand Up @@ -162,7 +162,7 @@ <h2>Where to put it</h2>
itself. To type <code>deed</code> instead of a path, move it onto
yours:
</p>
<pre class="code"><code>$ sudo mv deed-v0.2.2-x86_64-unknown-linux-gnu/deed /usr/local/bin/deed</code></pre>
<pre class="code"><code>$ sudo mv deed-v0.2.3-x86_64-unknown-linux-gnu/deed /usr/local/bin/deed</code></pre>
<p>
On Windows, put the folder on <code>Path</code> through Settings, or
keep using the full path. Nothing on this page needs it on
Expand Down Expand Up @@ -218,7 +218,7 @@ <h2>When it does not work</h2>
<code>./</code>, or move it as above.
</dd>

<dt><code>--version</code> prints something other than 0.2.2</dt>
<dt><code>--version</code> prints something other than 0.2.3</dt>
<dd>
You are running a different <code>deed</code> than the one you
unpacked. <code>which deed</code> says which.
Expand Down
4 changes: 2 additions & 2 deletions one-clause/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,14 @@ <h2 data-step="Now take the first one and add `Io.save` to it, and watch the com
<nav class="next" aria-label="Open in the playground">
<a
class="card"
href="../play/#0.2.2/zTZFBi9wwDIXv_hXv1i5Mk3soC0t7mcsW2oUei-MoY4MjBUveNJT-9-JktuzRsvS9p6e-xzcmhOyrEhLDQ9ONvdVCSAqLhCnNMxXiQBjJNiKGbYIgyypMbNq5vnd9j5eYFHPlYEkYhfyk8JhTpg4vkd6R1e8K-u2D5R1b9IZkCJ5hUkMcGqwpt1Hd1Wi5YJZyIBPfOjyLxcQ3UFbq8EWWNWWaYIKfND6p0jLm_dIYDbXIVDN9UKRllWJQOh0GYfOJFcIEWan4ozwXWQ75KGrDfTdgIpqGJDhc3KtPRw8S60rBWlzekJMaRpqlEEplbkZbrHzLLWK1Ug_9DlfDRCFNpA22RbJIpW2hdV3zDvIh3k13b1qHxFrktY3hKl2zg7EaWKxhrtKpf6UjzlIZ1q7y_1jYkkWphkJqJZ1GnDsjwkSL9A34Szjvzs3cpAKpfjxuMeBrKhewX2jADyuJbw_49IjvpDXb57Nyuf88OqAqqQPwZvTi_rx_ntQT-OD-un8"
href="../play/#0.2.3/zTZFBi9wwDIXv_hXv1i5Mk3soC0t7mcsW2oUei-MoY4MjBUveNJT-9-JktuzRsvS9p6e-xzcmhOyrEhLDQ9ONvdVCSAqLhCnNMxXiQBjJNiKGbYIgyypMbNq5vnd9j5eYFHPlYEkYhfyk8JhTpg4vkd6R1e8K-u2D5R1b9IZkCJ5hUkMcGqwpt1Hd1Wi5YJZyIBPfOjyLxcQ3UFbq8EWWNWWaYIKfND6p0jLm_dIYDbXIVDN9UKRllWJQOh0GYfOJFcIEWan4ozwXWQ75KGrDfTdgIpqGJDhc3KtPRw8S60rBWlzekJMaRpqlEEplbkZbrHzLLWK1Ug_9DlfDRCFNpA22RbJIpW2hdV3zDvIh3k13b1qHxFrktY3hKl2zg7EaWKxhrtKpf6UjzlIZ1q7y_1jYkkWphkJqJZ1GnDsjwkSL9A34Szjvzs3cpAKpfjxuMeBrKhewX2jADyuJbw_49IjvpDXb57Nyuf88OqAqqQPwZvTi_rx_ntQT-OD-un8"
>
<strong>read_only.deed</strong>
<span>The one a host without `Io.save` will run.</span>
</a>
<a
class="card"
href="../play/#0.2.2/zhVFBjtswDLz7FXNrArj2PWgDLNpLTwXaBXpcKBIdC2uThkhvNijy90Jy7F20h-okaciZ4bBt8Z0JfnCzEiLDQeOZnc2JEBXWE0LsOkrEnnAiuxAx7CLwMk7CxKZN1bZV2-Kxj4puZm9RGIlcUDh0cSA4DrikaKSIhpPzz00RpldL7p289ZSZNg8HfJNG3Qs1-CLjFAcKMMEvOj2o0ngarnXxOEqYB_qgiOMkyaBUTGQulgu8sLnIWozLRMllVNElGUt_L2qH-xhAIAqHKCgz_PWVzdwLH0obrHeGKclLDKQQHq7ZdG6FEi0Z5q7VW84iUTdrwaJmqi1MnKiTREgzc-Rz2QefhxyOWprLVA0et5ERyA8uUYADExW360DwjlkMI5HVRXZDluUU51sG_0Z39-L4Ci-hmNKmqjblUdpM9FQ2W1Ud5xg8qe7y0vWArzHVYDfSAT8tRT7v8fGIH6TzYJ-Wn_qOHCsgR1IBWPOr10eOr65-l-fozPdrxSK0aOyxFORDKe0u_XWPz8ftXm-oPO-MXq2gG13WeE9XY6l5I_0_8Z38qcCryht8W6636lb9AQ"
href="../play/#0.2.3/zhVFBjtswDLz7FXNrArj2PWgDLNpLTwXaBXpcKBIdC2uThkhvNijy90Jy7F20h-okaciZ4bBt8Z0JfnCzEiLDQeOZnc2JEBXWE0LsOkrEnnAiuxAx7CLwMk7CxKZN1bZV2-Kxj4puZm9RGIlcUDh0cSA4DrikaKSIhpPzz00RpldL7p289ZSZNg8HfJNG3Qs1-CLjFAcKMMEvOj2o0ngarnXxOEqYB_qgiOMkyaBUTGQulgu8sLnIWozLRMllVNElGUt_L2qH-xhAIAqHKCgz_PWVzdwLH0obrHeGKclLDKQQHq7ZdG6FEi0Z5q7VW84iUTdrwaJmqi1MnKiTREgzc-Rz2QefhxyOWprLVA0et5ERyA8uUYADExW360DwjlkMI5HVRXZDluUU51sG_0Z39-L4Ci-hmNKmqjblUdpM9FQ2W1Ud5xg8qe7y0vWArzHVYDfSAT8tRT7v8fGIH6TzYJ-Wn_qOHCsgR1IBWPOr10eOr65-l-fozPdrxSK0aOyxFORDKe0u_XWPz8ftXm-oPO-MXq2gG13WeE9XY6l5I_0_8Z38qcCryht8W6636lb9AQ"
>
<strong>read_write.deed</strong>
<span>The one it will not.</span>
Expand Down