← Commit history
docs: record approved Phase 0 language decisions
lennoxrose committed on Oct 17, 2026 at 16:38 UTC
1 file changed+422−0
MIGRATION.mdadded
+422−0@@ -0,0 +1,422 @@1
+# Ash → Enterprise Java-like Ash Migration Checklist2
+3
+This document defines the target design for Ash and the ordered migration plan.4
+5
+Ash must become a strictly enterprise-grade, Java-like, high-level, statically typed language.6
+It must always use classes, packages, explicit types, access modifiers, constructors, interfaces, enums, records, generics, and exceptions.7
+8
+The old Ash syntax is deprecated. The new syntax is the only supported application-level syntax.9
+10
+## Design principles11
+12
+- [ ] Ash is object-oriented at the application level.13
+- [ ] Ash is statically typed.14
+- [ ] Ash is explicit and predictable.15
+- [ ] Ash is suitable for backend services, desktop apps, games, CLI tools, and frontend libraries.16
+- [ ] Ash is familiar to Java, C#, Kotlin, and TypeScript developers.17
+- [ ] Ash is more ergonomic than Java, but never script-like.18
+- [ ] Ash is designed for eventual fast native compilation.19
+- [ ] Every program uses a package and a class-based entry point.20
+- [ ] `//` comments remain supported.21
+- [ ] `/* ... */` and `///` comments are supported.22
+23
+## Target syntax24
+25
+- [ ] Use `package` declarations.26
+- [ ] Use `import` declarations.27
+- [ ] Use `public`, `private`, `protected`, and package-private access modifiers.28
+- [ ] Use `class`, `interface`, `enum`, `record`, `abstract`, `sealed`, and `permits`.29
+- [ ] Use `extends` and `implements`.30
+- [ ] Use `new` for object creation.31
+- [ ] Use `this` and `super`.32
+- [ ] Use `static`, `final`, and `const`.33
+- [ ] Use `public static void main(String[] args)` as the standard entry point.34
+- [ ] Use `return`, `if`, `else`, `else if`, `switch`, `case`, `default`.35
+- [ ] Use `while`, `for`, `do`, `break`, and `continue`.36
+- [ ] Use `try`, `catch`, `finally`, `throw`, and `throws`.37
+- [ ] Use `true`, `false`, and `null`.38
+- [ ] Use `instanceof`.39
+- [ ] Use `var` only for obvious local type inference.40
+- [ ] Use Java-style generics: `List<Player>`, `Map<String, Player>`, `Set<T>`.41
+- [ ] Use lambdas and method references.42
+- [ ] Use `Optional<T>` and `Result<T, E>` where appropriate.43
+44
+## Keyword migration45
+46
+Complete this table and implement it consistently:47
+48
+| Old Ash | New Ash | Status |49
+|---|---|---|50
+| `local x = 5;` | `int x = 5;` or `var x = 5;` | [ ] |51
+| `say x;` | `System.out.println(x);` or `Console.println(x);` | [ ] |52
+| `given` | `if` | [ ] |53
+| `otherwise` | `else` | [ ] |54
+| `during` | `while` | [ ] |55
+| `each x in xs` | `for (Type x : xs)` | [ ] |56
+| `stop;` | `break;` | [ ] |57
+| `next;` | `continue;` | [ ] |58
+| `forge name(args)` | method declaration | [ ] |59
+| `yield value;` | `return value;` | [ ] |60
+| `attempt` | `try` | [ ] |61
+| `handle (e)` | `catch (Exception e)` | [ ] |62
+| `raise value;` | `throw new SomeException(...);` | [ ] |63
+| `yes` | `true` | [ ] |64
+| `no` | `false` | [ ] |65
+| `none` | `null` | [ ] |66
+| `@import <file.ash>;` | `import package.module;` | [ ] |67
+| `@import <file.ash> as x;` | `import package.Type;` (aliases are dropped, decision D1) | [ ] |68
+| `forge(args) { ... }` (anonymous) | lambda `(args) -> expr` or `(args) -> { ... }` | [ ] |69
+| `a and b` / `a or b` / `not a` | `a && b` / `a \|\| b` / `!a` | [ ] |70
+| `each x in xs` (`in`) | `:` separator; `in` becomes a plain identifier | [ ] |71
+| `e.message` | `e.getMessage()` | [ ] |72
+| `file.NAME` (module variable) | `static` field, `ClassName.NAME` | [ ] |73
+| builtin functions (`len`, `push`, ...) | stdlib methods (see Builtin mapping) | [ ] |74
+75
+## Type system76
+77
+- [ ] Define primitive types: `int`, `long`, `float`, `double`, `bool`, `char`, `byte`, `short`, `void`.78
+- [ ] Define core object types: `String`, `Object`, `Array<T>`, `List<T>`, `Map<K, V>`, `Set<T>`, `Optional<T>`, `Result<T, E>`.79
+- [ ] Require explicit parameter and return types on public methods.80
+- [ ] Allow `var` only for local variables with obvious inferred types.81
+- [ ] Require `final` for immutable locals and fields.82
+- [ ] Make fields `private` by default.83
+- [ ] Forbid public mutable fields except with explicit approval.84
+- [ ] Define nullable types using `?`, for example `String?`.85
+- [ ] Make non-nullable types reject possible null assignments at compile time.86
+- [ ] Define integer, floating-point, and mixed numeric behavior precisely.87
+- [ ] Define integer division and modulo behavior precisely.88
+- [ ] Define generic classes, interfaces, methods, and collections.89
+- [ ] Define type inference rules.90
+- [ ] Define casting rules.91
+- [ ] Define equality and hashing rules.92
+93
+## Object model94
+95
+- [ ] Support classes.96
+- [ ] Support interfaces.97
+- [ ] Support abstract classes.98
+- [ ] Support enums.99
+- [ ] Support immutable records.100
+- [ ] Support constructors.101
+- [ ] Support static fields and methods.102
+- [ ] Support instance fields and methods.103
+- [ ] Support method overloading.104
+- [ ] Support method overriding.105
+- [ ] Support `@Override`.106
+- [ ] Support interface default methods.107
+- [ ] Support sealed types.108
+- [ ] Support nested classes.109
+- [ ] Support generics.110
+- [ ] Support lambdas.111
+- [ ] Support method references.112
+- [ ] Support streams or an equivalent collection pipeline API.113
+- [ ] Do not make structs, traits, mixins, or top-level functions the primary application model.114
+115
+## Error handling116
+117
+- [ ] Use `try`, `catch`, `finally`, `throw`, and `throws`.118
+- [ ] Define checked exceptions for recoverable I/O, parsing, network, and domain failures.119
+- [ ] Define runtime exceptions for programming errors.120
+- [ ] Define a base `Exception` type.121
+- [ ] Define a base `RuntimeException` type.122
+- [ ] Require custom exceptions to extend the appropriate base exception.123
+- [ ] Require public methods to declare checked exceptions with `throws`.124
+- [ ] Define stack traces.125
+- [ ] Define error messages.126
+- [ ] Define exception chaining.127
+- [ ] Do not use hidden error values or implicit panics.128
+129
+## Standard library130
+131
+- [ ] Define `ash.lang`.132
+- [ ] Define `ash.util`.133
+- [ ] Define `ash.io`.134
+- [ ] Define `ash.net`.135
+- [ ] Define `ash.math`.136
+- [ ] Define `ash.time`.137
+- [ ] Define `ash.concurrent`.138
+- [ ] Define `ash.json`.139
+- [ ] Define `ash.cli`.140
+- [ ] Define `ash.test`.141
+- [ ] Define `ash.desktop`.142
+- [ ] Define `ash.game`.143
+- [ ] Define `ash.web`.144
+- [ ] Define `System.out.println`.145
+- [ ] Define `Console.println`.146
+- [ ] Define `String`, `StringBuilder`, `Math`, `Object`.147
+- [ ] Define `List<T>`, `ArrayList<T>`, `Map<K, V>`, `HashMap<K, V>`, `Set<T>`, `HashSet<T>`.148
+- [ ] Define `Optional<T>`, `Result<T, E>`, and `Stream<T>`.149
+- [ ] Define `Files`, `Path`, `Json`, `Http`, `Thread`, and `Executor`.150
+- [ ] Define collection methods or stream operations: `filter`, `map`, `reduce`, `sorted`, `toList`, `collect`, `forEach`.151
+152
+## Formatting rules153
+154
+- [ ] Use four-space indentation.155
+- [ ] Put `{` on the same line as the declaration or statement.156
+- [ ] Put `}` on its own line.157
+- [ ] Use one statement per line.158
+- [ ] Use `PascalCase` for classes, interfaces, enums, and records.159
+- [ ] Use `lowerCamelCase` for methods, variables, fields, and parameters.160
+- [ ] Use `UPPER_SNAKE_CASE` for constants.161
+- [ ] Use lowercase dot-separated package names.162
+- [ ] Use `isX()` for boolean getters.163
+- [ ] Use `getX()` and `setX()` only when valuable.164
+- [ ] Use `of()`, `from()`, `parse()`, and `create()` for factory methods.165
+- [ ] Use `toString()`, `toInt()`, and similar conversion names consistently.166
+167
+## Performance model168
+169
+- [ ] Choose the first implementation strategy: interpreter, bytecode VM, or direct tree-walking evaluator.170
+- [ ] Choose the long-term execution strategy: AOT native compilation, JIT, or hybrid.171
+- [ ] Define whether Ash uses tracing GC, reference counting, arenas, or a hybrid memory model.172
+- [ ] Define primitive value representation.173
+- [ ] Define object representation.174
+- [ ] Define string representation.175
+- [ ] Define array/list representation.176
+- [ ] Define map representation.177
+- [ ] Define function/closure representation.178
+- [ ] Avoid unnecessary boxing.179
+- [ ] Support value-like records.180
+- [ ] Define concurrency: threads, executors, synchronization, and optional async/await.181
+- [ ] Define startup-time and memory predictability requirements for games and desktop applications.182
+183
+## Approved decisions (Phase 0)184
+185
+| ID | Decision |186
+|---|---|187
+| D1 | `import ... as` is dropped. Imports name a type or package: `import ash.util.List;`, `import ash.util.*;`. |188
+| D2 | The old front end stays frozen and runnable (`.ash` files without a `package`/`class` header go through it) until the new front end reaches parity; then it is deleted. New front end is selected per file by syntax detection (a file whose first token is `package`, `import`, `public`, `class`, `interface`, `enum`, `record`, `abstract`, `final` or `sealed` is new Ash). |189
+| D3 | Old syntax is diagnosed by context (e.g. `say` followed by an expression start at statement position), not by reserving the words. `next`, `stop`, `each`, `no`, `in`, `as`, `say` ... are ordinary identifiers in new Ash. |190
+| D4 | `T[]` is the surface array syntax; `Array<T>` is its canonical type name. |191
+| D5 | `System.out.println` is the print API. `Console.println` is removed from the spec. |192
+| D6 | `const` = `static final` compile-time constant. `final` alone = variable assigned once. |193
+| D7 | Default access: package-private for classes/methods/constructors, `private` for fields. Public mutable fields need `@PublicField`. |194
+| D8 | `T?` is the nullable type. `Optional<T>` for return values that may be absent. `Result<T, E>` exists only in the stdlib. Non-null `T` rejects `null` at compile time. |195
+| D9 | The REPL is an explicit exception: each line is wrapped in a hidden class body. It is not application syntax. |196
+| D10 | `} else {`, `} catch (...) {`, `} finally {` and `} while (...);` may share a line with `}` (Java style). |197
+| D11 | Memory model: tracing GC, reference semantics for objects. The `goals.md` ownership model is shelved. Until a collector lands, allocation is bump/never-free, as today. |198
+| D12 | One shared front end (lexer, parser, AST, type checker) lives in `front/` and is linked by both `ashvm` and `kiln`. |199
+| D13 | Primitive boolean type is spelled `bool`. |200
+| D14 | Checked exceptions are enforced (Exception subclasses not under RuntimeException must be caught or declared with `throws`). |201
+| D15 | Performance (CLAUDE.md law): no change may make the recorded baselines slower. Baselines are recorded in `docs/performance-baseline.md` before runtime changes. |202
+203
+## Specification204
+205
+### Keywords206
+207
+Reserved: `package import class interface enum record extends implements permits sealed abstract new this super public private protected static final const native if else switch case default while for do break continue return try catch finally throw throws instanceof void true false null int long float double bool char byte short`.208
+Contextual: `var`, `record`, `sealed`, `permits`. `else if` is `else` followed by `if`.209
+210
+### Operators (highest precedence first)211
+212
+| Level | Operators | Assoc |213
+|---|---|---|214
+| 1 | `() [] . :: x++ x--` | left |215
+| 2 | `++x --x +x -x ! ~ (Type)cast new` | right |216
+| 3 | `* / %` | left |217
+| 4 | `+ -` | left |218
+| 5 | `<< >> >>>` | left |219
+| 6 | `< <= > >= instanceof` | left |220
+| 7 | `== !=` | left |221
+| 8 | `&` | left |222
+| 9 | `^` | left |223
+| 10 | `\|` | left |224
+| 11 | `&&` | left |225
+| 12 | `\|\|` | left |226
+| 13 | `?:` | right |227
+| 14 | `= += -= *= /= %= &= \|= ^= <<= >>=` | right |228
+| 15 | `->` (lambda) | right |229
+230
+`&&` and `||` short-circuit. Conditions of `if`/`while`/`for`/`?:` must be `bool`; numbers, strings and `null` are not truthy.231
+232
+### Numerics233
+234
+- `int` is 32-bit two's complement, `long` 64-bit, `short` 16, `byte` 8, `char` 16-bit unsigned, `float` 32-bit IEEE, `double` 64-bit IEEE. Integer overflow wraps. Division by zero for integers throws `ArithmeticException`; for floating point it yields inf/NaN.235
+- Integer `/` truncates toward zero; `%` takes the sign of the dividend (`-7 % 3 == -1`).236
+- Mixed arithmetic widens: byte/short/char -> int -> long -> float -> double. Narrowing needs an explicit cast.237
+- Literals: `42` int, `42L` long, `1.5` double, `1.5f` float, `1e3` double, `0x1F`, `'a'` char, escapes `\n \t \r \\ \" \' \0 \xHH`.238
+- `double` prints in shortest round-trip form with a mandatory `.0` for whole values (`5.0`); the old Ash "plain integer" format is retired for doubles. `int`/`long` print as integers.239
+- String `+` converts the other operand with `toString()`.240
+241
+### Nullability242
+243
+`T` is non-null; `T?` may be `null`. Assigning a `T?` to `T`, dereferencing a `T?` without a check, or passing `null` for `T` is a compile error. `x != null` narrows `x` inside the guarded branch. `Optional<T>` is a library type, not a replacement for `T?`.244
+245
+### Exceptions246
+247
+`Object <- Throwable <- Exception <- RuntimeException`. Checked: `Exception` subclasses outside `RuntimeException` (`IOException`, `ParseException`, ...). Unchecked: `RuntimeException` and subclasses (`IndexOutOfBoundsException`, `KeyNotFoundException`, `ArithmeticException`, `NullPointerException`, `ClassCastException`, `IllegalArgumentException`, `IllegalStateException`). Chaining: `new X(message, cause)`, `getCause()`. Stack traces: `printStackTrace()` prints `Type: message` then ` at Class.method(File.ash:line)` per frame. An uncaught exception prints that trace to stderr and exits with status 1. Runtime error text of the old VM maps as: `index out of bounds` -> `IndexOutOfBoundsException`, `key not found` -> `KeyNotFoundException`.248
+249
+### Packages and compilation units250
+251
+One public top-level type per file; file name equals the type name plus `.ash`. A package maps to a directory under the source root (`src/main/ash/com/acme/Main.ash` is `package com.acme;`). The program entry is the class named on the command line (or the sole class declaring `public static void main(String[] args)`). `ash.lang` is imported implicitly. Dependencies installed by forgepack (`ash.libs/`, `@ash-modules/`) are additional source roots; `ash.pkg` format is unchanged.252
+253
+### Types254
+255
+Primitives: `int long float double bool char byte short void`. Object types: `String Object Array<T> List<T> ArrayList<T> Map<K,V> HashMap<K,V> Set<T> HashSet<T> Optional<T> Result<T,E> Stream<T> StringBuilder Math`. Functional interfaces in `ash.lang.function`: `Function<T,R> Predicate<T> Supplier<T> Consumer<T> BiFunction<A,B,R> Runnable Comparator<T>`. Generics use erasure: type arguments are checked at compile time and not materialised at runtime; primitive type arguments are not allowed (use `Integer`, `Double` etc. boxes, which the VM represents as unboxed tagged values to avoid allocation).256
+257
+### Object model details258
+259
+- Enums may have fields, constructors, methods; `values()`, `ordinal()`, `name()`, `valueOf(String)`. Switch over an enum or sealed type must be exhaustive when used as an expression.260
+- Records: `record Point(int x, int y) { }` generate private final fields, accessors `x()`, canonical constructor (compact form allowed), `equals`, `hashCode`, `toString`.261
+- Interfaces: abstract methods, `default` methods, `static` methods. An interface with a single abstract method is a functional interface and is the target type for lambdas and method references.262
+- Switch: statement form `case X:` and expression form `case X -> expr;`. No `yield`; a block arm uses `return`-free `-> { ... }` only in statement switches; expression switches use single-expression arms.263
+- Lambdas capture effectively-final locals by value.264
+- Overloading resolves by arity then most specific applicable parameter types; ambiguity is a compile error.265
+- `equals`/`hashCode`: `Object` defaults to identity; strings, boxes, records and enums define value equality.266
+- `native` methods bind to C functions through the C ABI in `ashvm/src/builtins` and `kiln` runtime.267
+268
+### Builtin mapping269
+270
+| Old builtin | New API |271
+|---|---|272
+| `len(s)`, `len(a)` | `s.length()`, `a.length` / `list.size()` / `map.size()` |273
+| `str(x)` | `String.valueOf(x)` / `x.toString()` |274
+| `num(s)` | `Double.parse(s)` / `Integer.parse(s)` |275
+| `type(x)` | `x.getClass().getName()` |276
+| `sqrt abs floor` | `Math.sqrt Math.abs Math.floor` |277
+| `chr ord` | `(char) n`, `(int) c` |278
+| `input()` | `System.in.readLine()` |279
+| `exit(n)` | `System.exit(n)` |280
+| `push pop insert slice delete sort` | `list.add list.removeLast list.add(i,x) list.subList list.remove list.sort` |281
+| `keys values has` | `map.keys() map.values() map.containsKey` |282
+| `map filter reduce` | `list.stream().map/filter/reduce` |283
+| `split join substring indexOf contains starts_with ends_with replace repeat upper lower trim` | `split String.join substring indexOf contains startsWith endsWith replace repeat toUpperCase toLowerCase trim` |284
+| `read_file write_file append_file file_exists rename_file delete_file make_dir list_dir` | `Files.readString writeString appendString exists move delete createDirectory list` (throw `IOException`) |285
+286
+### Formatting287
+288
+Four spaces, `{` same line, `}` own line except `} else`/`} catch`/`} finally`/`} while`, one statement per line, Java naming, `isX()` boolean getters.289
+290
+### Transition and performance protocol291
+292
+1. Old front end remains runnable (D2); the regression suites for old tests must stay byte-identical until the old path is deleted.293
+2. New tests live beside old ones in `tests/cases/<feature>/` with names ending `_j.ash`.294
+3. Before runtime changes, `docs/performance-baseline.md` records `fib(30)` and sum-to-20M for `ashvm`; they are re-measured after each major runtime change.295
+296
+## Migration phases297
+298
+### Phase 0 — Specification lock299
+300
+- [ ] Finalize the complete keyword list.301
+- [ ] Finalize operator precedence and associativity.302
+- [ ] Finalize primitive and object types.303
+- [ ] Finalize nullability rules.304
+- [ ] Finalize exception model.305
+- [ ] Finalize package/import model.306
+- [ ] Finalize class/interface/enum/record model.307
+- [ ] Finalize formatting rules.308
+- [ ] Produce before/after examples for every old construct.309
+310
+### Phase 1 — Lexer migration311
+312
+- [ ] Remove old keywords from the lexer.313
+- [ ] Add all new keywords.314
+- [ ] Add new operators and punctuation where needed.315
+- [ ] Preserve `//` comments.316
+- [ ] Add `/* ... */` comments.317
+- [ ] Add `///` documentation comments.318
+- [ ] Update lexer tests.319
+- [ ] Ensure old Ash programs fail with clear migration errors.320
+321
+### Phase 2 — Parser and AST migration322
+323
+- [ ] Parse package declarations.324
+- [ ] Parse imports.325
+- [ ] Parse classes.326
+- [ ] Parse interfaces.327
+- [ ] Parse enums.328
+- [ ] Parse records.329
+- [ ] Parse fields.330
+- [ ] Parse constructors.331
+- [ ] Parse methods.332
+- [ ] Parse access modifiers.333
+- [ ] Parse static and final modifiers.334
+- [ ] Parse generics.335
+- [ ] Parse `new` expressions.336
+- [ ] Parse `this` and `super`.337
+- [ ] Parse Java-style control flow.338
+- [ ] Parse enhanced for loops.339
+- [ ] Parse switch statements and/or expressions.340
+- [ ] Parse try/catch/finally.341
+- [ ] Parse throw and throws.342
+- [ ] Parse lambdas.343
+- [ ] Parse method references.344
+- [ ] Update AST tests.345
+346
+### Phase 3 — Type system347
+348
+- [ ] Implement primitive type checking.349
+- [ ] Implement class and interface type checking.350
+- [ ] Implement generics.351
+- [ ] Implement nullability checking.352
+- [ ] Implement access-control checking.353
+- [ ] Implement inheritance and override checks.354
+- [ ] Implement interface implementation checks.355
+- [ ] Implement method overload resolution.356
+- [ ] Implement casting and `instanceof`.357
+- [ ] Implement type inference for `var`.358
+- [ ] Produce clear type errors.359
+360
+### Phase 4 — Compiler/VM execution361
+362
+- [ ] Compile or interpret class declarations.363
+- [ ] Compile or interpret constructors.364
+- [ ] Compile or interpret methods.365
+- [ ] Compile or interpret static members.366
+- [ ] Compile or interpret object creation.367
+- [ ] Compile or interpret inheritance and interfaces.368
+- [ ] Compile or interpret exceptions.369
+- [ ] Compile or interpret generics.370
+- [ ] Compile or interpret lambdas and closures.371
+- [ ] Compile or interpret collections.372
+- [ ] Implement `public static void main(String[] args)`.373
+- [ ] Update runtime tests.374
+375
+### Phase 5 — Standard library376
+377
+- [ ] Implement `ash.lang`.378
+- [ ] Implement `ash.util`.379
+- [ ] Implement `ash.io`.380
+- [ ] Implement `ash.math`.381
+- [ ] Implement `ash.time`.382
+- [ ] Implement `ash.concurrent`.383
+- [ ] Implement `ash.json`.384
+- [ ] Implement `ash.cli`.385
+- [ ] Implement `ash.test`.386
+- [ ] Add documentation for every public API.387
+388
+### Phase 6 — Examples and documentation389
+390
+- [ ] Hello world.391
+- [ ] Variables and primitives.392
+- [ ] Classes and constructors.393
+- [ ] Interfaces and implementations.394
+- [ ] Inheritance and abstract classes.395
+- [ ] Enums and switch.396
+- [ ] Records.397
+- [ ] Generics.398
+- [ ] Collections and streams.399
+- [ ] Lambdas and method references.400
+- [ ] Exceptions.401
+- [ ] File I/O.402
+- [ ] JSON.403
+- [ ] CLI program.404
+- [ ] Game program.405
+- [ ] Backend/server program.406
+- [ ] Desktop-style program.407
+- [ ] Update README.408
+- [ ] Update language reference.409
+- [ ] Update migration guide.410
+411
+## Definition of done412
+413
+The migration is complete when:414
+415
+- [ ] No old Ash keywords remain in the supported language.416
+- [ ] All examples use the new enterprise Java-like syntax.417
+- [ ] The lexer, parser, type checker, and runtime support the new model.418
+- [ ] Every public API has explicit types.419
+- [ ] Every recoverable I/O or parsing failure uses the defined exception model.420
+- [ ] Every language feature has at least one example and one test.421
+- [ ] The README documents the new language.422
+- [ ] `MIGRATION.md` reflects the final implemented state.\ No newline at end of file