diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab7bdc8..b2e4bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,13 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] + python: ['3.12'] + # The newest Python too, so a behaviour that differs between the ends of + # the supported range fails here (3.12's `asyncio.run` takes only a + # coroutine, where 3.14's takes any awaitable). + include: + - os: ubuntu-latest + python: '3.14' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 @@ -34,7 +41,7 @@ jobs: # which would quietly hollow out this job. - uses: actions/setup-python@v6 with: - python-version: '3.12' + python-version: ${{ matrix.python }} - uses: dtolnay/rust-toolchain@stable with: toolchain: '1.97.0' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e44727..3cce882 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,9 @@ You need two things. - **Rust.** The repository pins its toolchain in `rust-toolchain.toml`, and `rustup` selects it automatically inside the repo. The crate has no dependencies, so the first build is quick. - **Python 3.12 or newer** on your `PATH` as `python` or `python3`. The end-to-end tests run the - emitted Python, and emitted code uses `match` statements and PEP 701 f-strings. + emitted Python, and emitted code uses `match` statements and PEP 701 f-strings. CI runs them on + 3.12 on all three systems and on 3.14 on Linux, so a difference between the two ends of the + supported range fails there even when your local run passes. Then: diff --git a/DESIGN.md b/DESIGN.md index f047c48..15381bd 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -504,7 +504,9 @@ something to call. The MVP prelude is `print : 'a -> unit` and the unit-polymorp `truncate` lower to `math.floor`/`ceil`/`trunc` with `import math` — the extern dotted-target path — while staying *unqualified* Pyfun names), plus the **unit-aware roots `sqrt : float<'u^2> -> float<'u>`** (√area = length) **and `cbrt : float<'u^3> -> float<'u>`** (∛volume = length; see §8.2; lower to -`math.sqrt`/`math.cbrt` like the conversions), plus a `unit` type whose one value is written `()` (both lower to +`math.sqrt`/`math.cbrt` like the conversions; as in Python, the result is the platform C library's, and glibc's +`cbrt` is not correctly rounded, so `cbrt 27.0` prints `3.0000000000000004` on Linux and `3.0` on Windows: the +last digit of a float maths function can differ between systems), plus a `unit` type whose one value is written `()` (both lower to Python `None` — the natural result of an effectful call). It also seeds the **standard combinators** `id : 'a -> 'a`, `const : 'a -> 'b -> 'a`, `ignore : 'a -> unit`, and `flip : (a -> b -> c) -> b -> a -> c` (fully type-polymorphic; `id`/`const`/`ignore` are pure, while diff --git a/examples/hello.pyfun b/examples/hello.pyfun index 0586445..2ef0e14 100644 --- a/examples/hello.pyfun +++ b/examples/hello.pyfun @@ -269,7 +269,7 @@ let floor = area 4 5 # int # (`sqrt 2.0`, `cbrt 2.0` are rejected dimensionally). These two are the # only tractable unit-carrying power ops — `**` stays dimensionless. let side = sqrt 16.0 # float — 4.0, unit halved (m^2 -> m) -let edge = cbrt 27.0 # float — 3.0, unit thirded (m^3 -> m) +let edge = cbrt 64.0 # float — 4.0, unit thirded (m^3 -> m) # Float literals support scientific notation (`1e6`, `2.5e-3`) — the exponent sign # is part of the literal. Physics constants read naturally, units and all: @@ -457,7 +457,7 @@ print planck # 6.626e-34 (scientific notation) print rounded # 4 (round, float -> int) print truncated # 10 (truncate, unit-preserving) print side # 4.0 (unit-aware sqrt: float -> float) -print edge # 3.0 (unit-aware cbrt: float -> float) +print edge # 4.0 (unit-aware cbrt: float -> float) print parsedF # 2.71828 (String.toFloat) print cube # 27.0 (exponentiation) print tower # 512.0 (right-associative `**`)