Skip to content

docs: write the October 8 language decisions into the spec - #58

Merged
heyJordanParker merged 7 commits into
masterfrom
docs/decisions-oct-8
Oct 8, 2026
Merged

heyJordanParker merged 7 commits into
masterfrom
docs/decisions-oct-8

Conversation

@heyJordanParker

@heyJordanParker heyJordanParker commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Writes the October 8 language decisions into docs/spec.md, adds section 32 (Modules), rewrites section 28 and 28.1 (Lean files), and adds records 050 to 067 in docs/decisions/. Records 001, 014, 029, 030, 034, 040, 041 and 043 quoted replaced text and now match the spec.

Round-2 rules

  • A struct never changes: point.x = 5 and readonly struct are compile errors, and the copy-on-write bullets and the php-src PR [RFC] Implement structs (WIP) php/php-src#13800 reference are gone (section 10).
  • mago compile writes .sharp/, and also creates a missing .lean file beside a class that has a law, never rewriting an existing one (section 27).
  • "Topic modules" becomes "standard library namespaces" (sections 8 and 23), so "module" means only module { … }.
  • The full-name rule covers class names in code, and a module header names a namespace, so module : App.Shop compiles (section 23).
  • new (expr)(args) is the form for a class value: new type(key) is now new (type)(key) everywhere (section 25).
  • private readonly Money total is a compile error (section 13).
  • Environment.require("NAME") is a static method, listed with Environment's other members (section 29).
  • The binding's lifetime decides how often a foreign object is created, and "created once" is gone (sections 29 and 32).
  • A raw generic object's type arguments are its bounds, so a raw page is a PaginatedList<DatabaseEntity>, and a method that takes any page is generic (section 11).
  • Plain PHP reads type arguments from objects only, with ReflectionObject::getTypeArguments() (section 11).
  • Console covers standard output and standard error, Files covers every other file, and Process covers exit, and both new effects join section 29's list.
  • Factory<T> is public interface Factory<T> { T create(); }, bound as in scoped Mailer = MailerFactory; (section 32).
  • A scoped lifetime is one HTTP request, one queued job or one console command run, started and ended by the host (section 32).
  • An environment module is Module.<Environment>.sharp, picked by APP_ENV, and a test module is declared in the test file that uses it (section 32).
  • A law's generated Lean statement is named by its class's full name and the law's name, as in App.Shared.Money.addKeepsCurrency (section 28.1).
  • Section 24's Names example keeps PR docs: specify events as types that listeners name in a trailing on clause #51's event syntax.

Round-3 rules

  • A module law's Lean statement is named by its namespace, Module and the law's name, and section 28.1 proves App.Tenant.Store.Module.receiptMatchesRefunds in the same shape as the class law.
  • Confirmed as written: theorem addKeepsCurrency : App.Shared.Money.addKeepsCurrency, the bound on the generic count, and Point's { get; init; } with with.
  • The Open headings in sections 12, 15 and 28.1 are gone, because each named a standard library API (keys() and entries(), the dispatcher, the structure facts). The library specifies those itself, tracked in php-sharp feat: raise events with emit and run listeners named by on clauses #56 (dispatcher) and feat: check Lean laws and structure rules when PHP# compiles #53 (structure facts).

Round-4 rule

Round-5 rule

  • Each Lean proof example in section 28.1, and in record 057's chosen option, shows its whole file, import Code.<namespace>.<Class> and open Sharp included, in the shape of the Lean design's "The proof file". Module.lean imports Code.App.Tenant.Store.Module and shows everything except its structure rule, whose Lean form waits on php-sharp feat: check Lean laws and structure rules when PHP# compiles #53.

Round-6 rule

  • Record 057's rejected old-layout example, app/Tenant/Store.lean, shows that layout exactly as it was, with no import or open lines. Console and Process stay standard foreign classes.

Merge order

PR #51 merges first. Read against #51's head 9234071b57, the two PRs touch adjacent lines in three places. In each one, keep both sides:

feat/slice-generics

When feat/slice-generics merges, it must delete two sentences that contradict records 054 and 061:

  • Section 25: "A class value of a generic class can't be created with new yet".
  • Section 11: "A static member can't use its class's type parameters".

Problem: the Architect approved a batch of language decisions, and the spec
still described several of them the old way, such as one Lean rules file per
namespace, foreign objects created where the app starts, and structs that
change through copy-on-write.

Solution: docs/spec.md states each decision in the section it belongs to,
adds section 32 (Modules), rewrites section 28 and 28.1 (Lean files), and
applies the orchestrator's round-2 rulings. Records 050 to 067 are new.
Records 001, 014, 029, 030, 034, 040, 041 and 043 quoted replaced text and
now match the spec.
…dings

Problem: section 28.1 showed no module law's proof, because a module law's
Lean name was undecided. Sections 12, 15 and 28.1 still held Open headings
that named standard library APIs, which the library specifies itself.

Solution: a module law's Lean statement is named by its namespace, Module and
the law's name, and section 28.1 proves App.Tenant.Store.Module.receiptMatchesRefunds.
The Open headings in sections 12, 15 and 28.1 are gone. Records 057 and 066
state both rules.
Problem: section 28.1's Module.lean example wrote its structure rule with
Sharp.importsOf, an API the standard library has not specified (php-sharp #53).

Solution: section 28.1 states the rule in words: "App.Tenant.Store imports
only App.Tenant.Community and App.Shared.Schema." The Lean example keeps
only the module law's proof.
Problem: section 28.1 and record 057 showed only the proof lines, so a reader
who copied a Lean example got a file that does not compile without its import
and open lines.

Solution: each proof example opens with its import and open Sharp, the shape
sharp/agents/130-lean-design/design.md gives in "The proof file". Money.lean
imports Code.App.Shared.Money. Module.lean imports Code.App.Tenant.Store.Module,
because Module.sharp states a law, and holds no rule code, so it imports no
Sharp.Lean.Facts.
Problem: round 5 added import and open lines to record 057's rejected
app/Tenant/Store.lean example, so the record no longer showed the old layout
as it was.

Solution: the rejected example drops those two lines again and matches its
first version.
Brings in the events rewrite (#51). In section 29, a class's effects now
include the events its methods emit, and a foreign object is still created
in a module binding, not once where the app starts.

docs/
└── spec.md*    <- section 29 keeps both the events and the module rule
The spec still gave unclassified built-in functions a catch-all effect,
Php, after the rulings removed it. A method calling one could never be
pure, and the signature could not say what the call does. Every built-in
now ships an extern declaration with its real effect, so the effect is
always known, and a missing declaration is a compile error that names it.

A plain PHP object a declared call returns had no effect, so
StripeClient.charges().create(...) could not fit uses Http. It now carries
the effect of the call that returned it, and an extern on the returned
class wins, as Kotlin treats Java's return values.

docs/
├── spec.md*                              <- section 29 drops Php, adds returned objects
└── decisions/
    └── 043-built-in-effects.md*          <- every built-in declared, no catch-all
@heyJordanParker
heyJordanParker merged commit 71125f2 into master Oct 8, 2026
7 checks passed
heyJordanParker added a commit that referenced this pull request Oct 8, 2026
Brings in the October 8 decisions (#58). Section 27 keeps master's Lean
wording: a law without a proof stops the file, and the old "rules file
stops its namespace" sentence is gone.

docs/
└── spec.md*    <- section 27 takes the new Lean rule beside the refusal list
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant