Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 73 additions & 3 deletions Docs/04-advanced-features/containers-oop.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,14 @@ buddy.make_sound()

## Interfaces

Interfaces define contracts that containers must fulfill:
Interfaces define contracts that containers must fulfill. An interface body
lists the actions every implementing container is **required** to provide:

```wfl
create interface Drawable
create interface Drawable:
requires action draw
requires action get_area: Number
end

create container Rectangle implements Drawable:
property width: Number
Expand All @@ -254,6 +258,72 @@ store area as rect.get_area()
display "Area: " with area
```

**Syntax:**
```wfl
create interface <Name>:
requires action <name>
requires action <name>: <ReturnType>
requires action <name> needs <param>: <Type>, <param>: <Type>
end
```

### Contracts Are Enforced

A container that claims `implements X` but does not provide every required
action is rejected. The static checker reports the breach, and the program
stops with an error when the container definition runs:

```wfl
create interface Drawable:
requires action draw
end

create container Circle implements Drawable:
property radius: Number
end

// Error: Container 'Circle' does not satisfy interface 'Drawable':
// missing required action 'draw'
```

A required action with parameters must be implemented with the same number of
parameters. A requirement may also be satisfied by an action inherited from a
parent container (`extends`).

Two details of the contract:

- **Interface contracts are instance contracts.** A `static action` with the
right name does not satisfy `requires action` — the requirement must be met
by a regular (instance) action.
- **Required return types are checked statically.** If an interface declares
`requires action get_area: Number` and the implementing action returns
`Text`, the static checker reports the mismatch before the program runs.

### Interface Inheritance

Interfaces can extend other interfaces; the requirements accumulate:

```wfl
create interface Drawable:
requires action draw
end

create interface Shape extends Drawable:
requires action get_area: Number
end

// A container implementing Shape must provide BOTH draw and get_area.
```

### Marker Interfaces

An interface without a body is an empty contract — useful as a marker or tag
that any container can implement:

```wfl
create interface Serializable
```

## Complete Example: Task Manager

```wfl
Expand Down Expand Up @@ -358,7 +428,7 @@ In this section, you learned:
✅ **Creating instances** - `create new`
✅ **Calling actions** - `object.action()`
✅ **Inheritance** - `extends` keyword
✅ **Interfaces** - `implements` keyword
✅ **Interfaces** - `implements` keyword, contracts enforced via `requires action`
✅ **Complete examples** - Task manager with OOP

## Next Steps
Expand Down
8 changes: 4 additions & 4 deletions Docs/reference/reserved-keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ These keywords **MUST** always be reserved and **CANNOT** be used as variable na
| `if` | Conditional | `check if x is 5:` |
| `implements` | Interface implementation | `container Dog implements Animal:` |
| `in` | For each collection | `for each item in list:` |
| `interface` | Interface definition | `define interface called Runnable:` |
| `interface` | Interface definition | `create interface Runnable:` |
| `load` | Load module | `load module math` |
| `module` | Module reference | `load module fs` |
| `not` | Logical NOT | `check if not x:` |
Expand All @@ -175,7 +175,7 @@ These keywords **MUST** always be reserved and **CANNOT** be used as variable na
| `public` | Public visibility | `public property name` |
| `push` | Add to list | `push with myList and item` |
| `repeat` | Loop construct | `repeat 10 times:` |
| `requires` | Interface requirement | `requires method run` |
| `requires` | Interface requirement | `requires action run` |
| `return` | Return value | `return result` |
| `route` | Dispatch on a value (match/switch) | `route path:` |
| `skip` | Continue (alias) | `skip` |
Expand Down Expand Up @@ -629,7 +629,7 @@ Complete reference table of all 181 keywords.
| `if` | Structural | Control Flow | ❌ | `check if` |
| `implements` | Structural | OOP | ❌ | `implements interface` |
| `in` | Structural | Control Flow | ❌ | `for each in` |
| `interface` | Structural | OOP | ❌ | `define interface` |
| `interface` | Structural | OOP | ❌ | `create interface` |
| `into` | Other | Process | ❌ | `output into` |
| `is` | Other | Comparison | ❌ | `x is 5` |
| `kill` | Other | Process | ❌ | `kill process` |
Expand Down Expand Up @@ -683,7 +683,7 @@ Complete reference table of all 181 keywords.
| `repeat` | Structural | Control Flow | ❌ | `repeat 10 times` |
| `replace` | Other | Pattern | ❌ | `replace pattern` |
| `request` | Other | Web/Network | ❌ | `HTTP request` |
| `requires` | Structural | OOP | ❌ | `requires method` |
| `requires` | Structural | OOP | ❌ | `requires action` |
| `respond` | Other | Web/Network | ❌ | `respond to request` |
| `response` | Other | Web/Network | ❌ | `HTTP response` |
| `return` | Structural | Operations | ❌ | `return value` |
Expand Down
111 changes: 111 additions & 0 deletions History/dev-diary/2026/2026-08-13-interface-contracts-enforced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# 2026-08-13 — Interfaces stop being decorative

## What changed

The containers doc (`Docs/04-advanced-features/containers-oop.md`) promised
"Interfaces define contracts that containers must fulfill." Auditing every
example in that page against the release binary showed the examples themselves
all ran and printed exactly what the doc claims — but the interface promise was
false. `create interface X` parsed only as a bare declaration: no body, no
required actions, and nothing anywhere in the pipeline ever checked that a
container claiming `implements X` provided anything at all. Even
`implements TotallyUndefinedInterface` executed happily at runtime (only the
type checker warned).

Interfaces are now real contracts:

```wfl
create interface Drawable:
requires action draw
requires action get_area: Number
end
```

- **Parser** — interface bodies with `requires action <name>`, optional
`needs` parameter lists, optional `: ReturnType`, and `extends` between
interfaces (comma-separated list). The `requires` keyword existed in the
lexer since the beginning and was never consumed by the parser. Bare
`create interface Name` still parses as an empty contract, so existing
programs (marker interfaces) keep working.
- **Interpreter** — when a `create container … implements …` definition is
evaluated, every required action (accumulated through interface `extends`
chains) must be present with the same parameter count, either on the
container itself or inherited through its own `extends` chain. A breach is
a runtime error naming the container, the interface, and the missing or
mismatched action; an unknown or non-interface name in `implements` is also
an error now.
- **Analyzer/Type checker** — the analyzer records an `InterfaceInfo`
registry, and the type checker performs the same conformance check
statically so tooling (LSP, MCP, `wfl --analyze` users) sees the breach
before execution.

## Dead code removed

`src/parser/container_ast.rs` (181 lines of duplicate AST definitions) and
`src/parser/container_parser.rs` (an empty comment stub) were never declared
as modules anywhere — not compiled, not referenced. Both deleted.

## TDD evidence

Red commit `test: red tests for interface contract parsing and enforcement`
adds `tests/interface_contract_test.rs`; six of its tests fail against the
prior implementation (body parsing, extends parsing, missing-action rejection,
unknown-interface rejection, inherited satisfaction, conformant execution) and
all pass after the change. Risk class R3 (backward compatibility): the bare
interface form and the entire `TestPrograms/` suite were re-run against the
release binary, and every code example in the containers doc was executed
before and after.

## Coverage added

- `tests/interface_contract_test.rs` — parser + end-to-end binary tests.
- `TestPrograms/containers/interface_contracts.wfl` — positive coverage:
bodies, extends accumulation, parameterized requirements, inherited
satisfaction, marker interfaces.
- `TestPrograms/error_examples/interface_missing_action.wfl` — gated
expected-failure program.
- `TestPrograms/docs_examples/containers/` — four registered doc examples
(basic container, interface contract, enforcement error, task manager)
wired into `validate_docs_examples.py`.
- `TestPrograms/containers_comprehensive.wfl` — its interface section now
uses a real body, so the flagship container test exercises enforcement.

## Doc honesty

The Interfaces section of the containers doc now shows the enforced syntax,
the error a breach produces, interface inheritance, and marker interfaces.
The keyword references had two stale examples (`define interface called
Runnable:`, `requires method run`) that matched no grammar past or present;
both now show the real forms.

## Review follow-up (same day)

Automated reviewers on PR #686 surfaced real gaps, fixed red-first in a
follow-up commit:

- **Fixer round-trip** — `wfl --lint --fix --in-place` rewrote interface
declarations into a grammar the parser rejects (`with a as T and b as T`,
`returns`, `end interface`); the fixer now emits the shipped grammar and
spells bare interfaces without a body.
- **Static/runtime parity on extends chains** — the type checker silently
skipped unknown or non-interface names reached through interface `extends`,
so `create interface Child extends NotAnInterface` passed static checks and
only failed at runtime. It reports them now.
- **No false positives on unresolvable parents** — a container whose
`extends` parent the analyzer cannot see (e.g. from `include from`) no
longer draws a bogus "missing required action" diagnostic; conformance
defers to the runtime check, which resolves parents from the live
environment.
- **Return-type conformance** — `requires action get_area: Number` is now
checked statically against the implementing action's declared or inferred
return type (Unknown/Any stay permissive, per gradual typing).
- **Diagnostics** — chain errors name the interface the container actually
implements; an unterminated interface body reports a real position instead
of 0:0.
- **Manifest schema** — `expected_failure_layer` now admits 5 (runtime), and
the enforcement error example declares it.

Deliberately not changed: the analyzer's interface registry is keyed by name
(not lexical binding), matching the existing container registry; a nested
shadowing interface could confuse static diagnostics, but the runtime check
scopes correctly. A scoped registry for both is a candidate follow-up.
136 changes: 136 additions & 0 deletions TestPrograms/containers/interface_contracts.wfl
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Interface contract test - interfaces with required actions are enforced
// when a container definition claims to implement them.

display "=== Interface Contract Test ==="

// === Interface with a body: required actions ===
create interface Drawable:
requires action draw
requires action get_area: Number
end

create container Rectangle implements Drawable:
property width: Number
property height: Number

action draw:
display "Drawing rectangle: " with width with " x " with height
end

action get_area: Number
return width times height
end
end

create new Rectangle as rect:
width is 10
height is 5
end

rect.draw()
store area as rect.get_area()
check if area is equal to 50:
display "PASS: area is 50"
otherwise:
display "FAIL: area is " with area
end check

// === Interface extends: requirements accumulate ===
create interface Shape extends Drawable:
requires action describe_shape: Text
end

create container Square implements Shape:
property side: Number

action draw:
display "Drawing square: " with side
end

action get_area: Number
return side times side
end

action describe_shape: Text
return "a square with side " with side
end
end

create new Square as sq:
side is 4
end

sq.draw()
store sq_desc as sq.describe_shape()
display "Square says: " with sq_desc
check if sq.get_area() is equal to 16:
display "PASS: square area is 16"
otherwise:
display "FAIL: square area is " with sq.get_area()
end check

// === Required action with parameters ===
create interface Resizable:
requires action resize needs w: Number, h: Number
end

create container Panel implements Resizable:
property width: Number
property height: Number

action resize needs w: Number, h: Number:
store width as w
store height as h
display "Panel resized to " with width with " x " with height
end
end

create new Panel as panel:
width is 1
height is 1
end

panel.resize(20, 30)

// === Inherited methods satisfy interface requirements ===
create interface Greeter:
requires action greet
end

create container Person:
property name: Text

action greet:
display "Hello, I am " with name
end
end

create container Employee extends Person implements Greeter:
property job_title: Text
end

create new Employee as bob:
name is "Bob"
job_title is "Developer"
end

bob.greet()

// === Bare interface: empty contract (backward compatible marker) ===
create interface Marker

create container Anything implements Marker:
property id: Number

action ping:
display "pong"
end
end

create new Anything as thing:
id is 1
end

thing.ping()

display "=== Interface Contract Test Completed ==="
5 changes: 4 additions & 1 deletion TestPrograms/containers_comprehensive.wfl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ display ""

// === Interface Implementation ===
display "3. Interface Implementation Test"
create interface Drawable
create interface Drawable:
requires action draw
requires action get_area: Number
end

create container Rectangle implements Drawable:
property width: Number
Expand Down
Loading
Loading