plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
bc54190
— Peter John
2026-10-27T17:49:04+05:30
docs(plans): primitive namespaces/built-in APIs, associated consts, Number removed
docs/plans/2026-10-27-statics-primitive.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Namespaced constants and statics: `primitive`, consts and statics in type bodies, `Number` removed
|
|
2
|
+
|
|
3
|
+
**Status:** approved by the user. It runs after the audit and the two fixes (`?` in closures, the formatter).
|
|
4
|
+
|
|
5
|
+
## User decisions
|
|
6
|
+
- Remove `enum Number = | Int | Float`. Its methods move to `extend Int` and `extend Float`, and the compiler's primitive-wrapping hack (`primitiveWrapVariant`, the implicit Int/Float→Number wrap) is deleted.
|
|
7
|
+
- Constants and static functions live only inside type bodies (enum, value enum), `extend` blocks and `primitive` namespaces. **Top-level consts are removed from the language.**
|
|
8
|
+
- The new keyword is `primitive Name =`: a stateless namespace holding constants and functions (Pony-style). It is usable in user code. It has no values, so `Name` can't be used as a type or constructed.
|
|
9
|
+
|
|
10
|
+
## Design
|
|
11
|
+
1. **Members.**
|
|
12
|
+
- Inside `enum`/`value enum` bodies, `extend` blocks and `primitive` bodies, `NAME = expr` declares an associated constant. A `fun` without `self` is a static function.
|
|
13
|
+
- Access: `Type.NAME`, `Type.fun(args)`, `Prim.NAME`, `Prim.fun(args)`. Inside the same body the bare name works.
|
|
14
|
+
- Associated consts keep today's lazy-global codegen (heap-valued consts are cached once), and names are unique per type, not globally.
|
|
15
|
+
2. **Std.**
|
|
16
|
+
- Number.plum constants go to their type: `Int.MIN`, `Int.MAX`, `Float.PI`, `Float.E`, `Float.TAU`, `Float.HALF_PI`, `Float.PHI`, `Float.LN2`, `Float.LN10`, `Float.LOG2E`, `Float.LOG10E`, `Float.SQRT2`, `Float.SQRT1_2`, `Float.SQRT_E`, `Float.SQRT_PI`, `Float.SQRT_PHI`, `Float.EPSILON`, `Float.MIN` (smallest positive), `Float.MAX`. `LARGE` is dropped if unused. Map's `BUCKET_COUNT` becomes `Map.BUCKET_COUNT` (private-by-convention) or a static.
|
|
17
|
+
- The free functions `parseInt`/`parseFloat` become statics `Int.parse(s)` and `Float.parse(s)`. Str's `parseInt` etc. stay as methods if they exist; ruling: keep one way, the `Type.parse(s)` statics, and remove the duplicates.
|
|
18
|
+
- Other std free functions are moved to namespaces only where they clearly belong (Time/Uuid/Random/Os/Http helpers become `primitive Time` etc., or statics on their types). Externs stay free, and a thin static wrapper can expose them.
|
|
19
|
+
3. **Grammar.** Add the `primitive` item, member consts in bodies, and static calls `Type.f()`, which already parse as attribute calls. Remove top-level const items, with no hints.
|
|
20
|
+
4. **Migrate** std, examples, README, embedded Rust tests, corpus, highlighting (`primitive` keyword), VS Code grammar, LSP (keywords, plus member completion for statics and consts after `Type.`), then reinstall the editors.
|
|
21
|
+
|
|
22
|
+
Tests:
|
|
23
|
+
- `primitive_consts_and_statics`;
|
|
24
|
+
- `enum_assoc_const`;
|
|
25
|
+
- `value_enum_assoc_const` (`Vec2.ZERO`);
|
|
26
|
+
- `extend_float_consts` (`Float.PI`);
|
|
27
|
+
- `int_parse_static`;
|
|
28
|
+
- `toplevel_const_removed` (plain parse error);
|
|
29
|
+
- `number_enum_removed` (Int and Float methods still work: `x.abs()`, `f.sqrt()`, `a.min(b)`);
|
|
30
|
+
- `primitive_not_a_type`;
|
|
31
|
+
- `assoc_const_names_per_type` (`A.X` and `B.X` coexist);
|
|
32
|
+
- `lsp_completes_statics`.
|
|
33
|
+
|
|
34
|
+
## Revision (user decision): `primitive` replaces `extend` (as in Pony, where built-in machine words are declared as primitives)
|
|
35
|
+
|
|
36
|
+
- `primitive X =` where X is a compiler built-in (Int, Float, Bool, Str, Byte, ByteSlice, Array, Seq[T]) declares that type's API (methods with `self`, `fun*` methods, statics and consts). It is std-only, replacing `extend X =`.
|
|
37
|
+
- `primitive X =` with any other name creates a stateless namespace (consts and statics only, no `self`), usable anywhere. It is not a value or a type, unlike Pony's singleton primitives, because Plum uses `enum` for markers and case sets.
|
|
38
|
+
- The `extend` keyword is deleted from the grammar, highlighting, VS Code grammar, LSP and docs, with no hints. Every `extend X =` in std becomes `primitive X =`.
|
|
39
|
+
|
|
40
|
+
Extra tests:
|
|
41
|
+
- `primitive_builtin_declares_methods` (`x.abs()` via `primitive Int`);
|
|
42
|
+
- `primitive_builtin_std_only` (user `primitive Int =` is an error);
|
|
43
|
+
- `primitive_namespace_self_rejected`;
|
|
44
|
+
- `extend_removed` (plain parse error).
|