From 2d955004460c5013e40ddebc1f4b608c6272b9ae Mon Sep 17 00:00:00 2001 From: "Sean T. Allen" Date: Thu, 26 Feb 2026 18:25:17 -0500 Subject: [PATCH 1/3] Add numeric fundamentals primer to Arithmetic section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Arithmetic tutorial page assumed readers already understood integer widths, signed vs. unsigned representation, and overflow/underflow. Since Pony may be a learner's first language where these concepts matter, this adds a short Numeric Fundamentals section before the Pony-specific material, with a skip-ahead link for experienced readers. Also fixes a factual error: "one's complement" was incorrect — Pony (and all modern hardware) uses two's complement for signed integers. The surrounding sentence was rewritten for clarity. Closes #486 --- CLAUDE.md | 12 +++++++ .../arithmetic-numeric-fundamentals.pony | 15 ++++++++ docs/expressions/arithmetic.md | 35 ++++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md create mode 100644 code-samples/arithmetic-numeric-fundamentals.pony diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..625f80a9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,12 @@ +# Pony Tutorial + +MkDocs-based tutorial site for the Pony programming language. + +## Local Verification + +- **mkdocs**: Run via venv (`pip install -r requirements.txt`, then `mkdocs serve` or `mkdocs build`) +- **cspell**: Run using a Docker container (CI uses `streetsidesoftware/cspell-action`; locally, use `docker run -v $(pwd):/workdir ghcr.io/streetsidesoftware/cspell:latest "docs/**/*.md"`) + +## Code Samples + +Code samples live in `code-samples/` and are included in docs via `--8<--` snippets inside fenced code blocks. Sample files are bare expressions (no `actor Main`) unless they need to compile standalone. diff --git a/code-samples/arithmetic-numeric-fundamentals.pony b/code-samples/arithmetic-numeric-fundamentals.pony new file mode 100644 index 00000000..6fddd8ff --- /dev/null +++ b/code-samples/arithmetic-numeric-fundamentals.pony @@ -0,0 +1,15 @@ +// Unsigned wrap-around: the value wraps from the maximum back to zero +U8(255) + 1 == 0 + +// Unsigned wrap-around: the value wraps from zero to the maximum +U8(0) - 1 == 255 + +// Signed integers: zero is in the middle of the range, not at the edge +// Compare with U8(0) - 1 above — for signed integers, this is just normal subtraction +I8(0) - 1 == -1 + +// Signed wrap-around: going past the maximum wraps to the minimum +I8(127) + 1 == -128 + +// Signed wrap-around: going past the minimum wraps to the maximum +I8(-128) - 1 == 127 diff --git a/docs/expressions/arithmetic.md b/docs/expressions/arithmetic.md index 1c3bf826..46745322 100644 --- a/docs/expressions/arithmetic.md +++ b/docs/expressions/arithmetic.md @@ -8,11 +8,44 @@ Pony focuses on two goals, performance and safety. From time to time, these two Pony provides different ways of doing arithmetic to give programmers the freedom to chose which operation suits best for them, the safe but slower operation or the fast one, because performance is crucial for the use case. +## Numeric Fundamentals + +Already familiar with integer widths, signed vs. unsigned, and overflow? [Skip ahead](#integers). + +### Integer Widths + +The number in a type name like `U8` or `I32` is the number of bits used to store the value. More bits means a wider range of representable values: + +| Type | Bits | Minimum | Maximum | +| ----- | ---- | ------- | ------- | +| `U8` | 8 | 0 | 255 | +| `I8` | 8 | -128 | 127 | +| `U32` | 32 | 0 | 4294967295 | +| `I32` | 32 | -2147483648 | 2147483647 | + +The pattern: an N-bit unsigned type can hold values from 0 to 2N - 1. An N-bit signed type splits its range roughly in half, covering -2N-1 to 2N-1 - 1. + +### Signed vs. Unsigned + +Unsigned integer types (`U8`, `U16`, `U32`, etc.) represent only non-negative values. All N bits contribute to the magnitude, so the maximum value for a `U8` is 255 (28 - 1). + +Signed integer types (`I8`, `I16`, `I32`, etc.) use two's complement representation, which gives them an asymmetric range. For example, `I8` covers -128 to 127, not -127 to 127. There is one more negative value than positive because zero takes up one of the non-negative slots. + +### Overflow and Underflow + +When an arithmetic operation produces a result outside the representable range, it overflows (too large) or underflows (too small). With fixed-width integers, wrap-around can occur: + +```pony +--8<-- "arithmetic-numeric-fundamentals.pony" +``` + +This wrap-around behaviour is a consequence of how fixed-width binary arithmetic works — the result is computed and then only the low N bits are kept. Different languages handle this differently: some leave it undefined, some throw an exception, and Pony wraps around by default. The next section covers Pony's specific approach in detail. + ## Integers ### Pony's default Integer Arithmetic -Doing arithmetic on integer types in Pony with the well known operators like `+`, `-`, `*`, `/` etc. tries to balance the needs for performance and correctness. All default arithmetic operations do not expose any undefined behaviour or error conditions. That means it handles both the cases for overflow/underflow and division by zero. Overflow/Underflow are handled with proper wrap around semantics, using one's complement on signed integers. In that respect we get behaviour like: +Doing arithmetic on integer types in Pony with the well known operators like `+`, `-`, `*`, `/` etc. tries to balance the needs for performance and correctness. All default arithmetic operations do not expose any undefined behaviour or error conditions. That means it handles both the cases for overflow/underflow and division by zero. Overflow and underflow are handled with wrap-around semantics, using two's complement representation for signed integers. In that respect we get behaviour like: ```pony --8<-- "arithmetic-default-integer-arithmetic.pony" From 2cd4df830d75f5983dbfc65ff8d2ecd707c68124 Mon Sep 17 00:00:00 2001 From: "Sean T. Allen" Date: Thu, 26 Feb 2026 18:31:59 -0500 Subject: [PATCH 2/3] Allow sup elements in markdown lint config The numeric fundamentals section uses for exponentiation notation (e.g., 2^N). The default MD033 rule disallows all inline HTML, so this allows the sup element specifically. --- .github/linters/.markdown-lint.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 7e403081..63482d07 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -1,5 +1,6 @@ { "MD007": { "indent": 4 }, "MD013": false, - "MD026": false + "MD026": false, + "MD033": { "allowed_elements": ["sup"] } } From f8468918889fdb01947f8ac644a442729164fb5f Mon Sep 17 00:00:00 2001 From: Sean T Allen Date: Wed, 4 Mar 2026 16:08:07 -0500 Subject: [PATCH 3/3] Apply suggestion from @jemc Co-authored-by: Joe Eli McIlvain --- code-samples/arithmetic-numeric-fundamentals.pony | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code-samples/arithmetic-numeric-fundamentals.pony b/code-samples/arithmetic-numeric-fundamentals.pony index 6fddd8ff..009b6d03 100644 --- a/code-samples/arithmetic-numeric-fundamentals.pony +++ b/code-samples/arithmetic-numeric-fundamentals.pony @@ -4,6 +4,9 @@ U8(255) + 1 == 0 // Unsigned wrap-around: the value wraps from zero to the maximum U8(0) - 1 == 255 +// Unsigned integer literal wrap-around: negative literals get wrapped as well +U8(-1) == 255 + // Signed integers: zero is in the middle of the range, not at the edge // Compare with U8(0) - 1 above — for signed integers, this is just normal subtraction I8(0) - 1 == -1