Skip to content

Commit a5dfc0e

Browse files
authored
Merge pull request #46 from mgomes/mgomes/v0-15-0
v0.15.0: type system v2
2 parents f34f2d6 + a6a4a14 commit a5dfc0e

17 files changed

+1441
-160
lines changed

ROADMAP.md

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ Release status legend:
2020
## Historical Completed Releases
2121

2222
These releases are already shipped and tagged.
23+
Completion dates reflect the corresponding git tag date.
2324

24-
### v0.1.0
25+
### v0.1.0 (completed 2025-12-15)
2526

2627
- [x] Added GitHub Actions CI workflow.
2728
- [x] Added stack traces to runtime errors.
@@ -30,95 +31,95 @@ These releases are already shipped and tagged.
3031
- [x] Added initial CLI support.
3132
- [x] Fixed package/versioning setup for release tagging.
3233

33-
### v0.2.0
34+
### v0.2.0 (completed 2025-12-15)
3435

3536
- [x] Added GoReleaser-based release automation.
3637
- [x] Added recursion depth limit enforcement.
3738
- [x] Added recursion limit test coverage.
3839

39-
### v0.2.1
40+
### v0.2.1 (completed 2025-12-15)
4041

4142
- [x] Pinned GoReleaser version for stable release builds.
4243

43-
### v0.2.2
44+
### v0.2.2 (completed 2025-12-15)
4445

4546
- [x] Adjusted GoReleaser configuration.
4647

47-
### v0.3.0
48+
### v0.3.0 (completed 2025-12-20)
4849

4950
- [x] Added `Number#times`.
5051
- [x] Added `Duration` class modeled after ActiveSupport-style duration semantics.
5152

52-
### v0.4.0
53+
### v0.4.0 (completed 2025-12-21)
5354

5455
- [x] Added duration arithmetic support.
5556
- [x] Improved optional-parens behavior for zero-arg methods.
5657
- [x] Implemented `Time` class.
5758

58-
### v0.4.1
59+
### v0.4.1 (completed 2025-12-21)
5960

6061
- [x] Renamed `Time#strftime` to `Time#format` for Go layout alignment.
6162
- [x] Added Duration and Time documentation.
6263

63-
### v0.5.0
64+
### v0.5.0 (completed 2025-12-26)
6465

6566
- [x] Added first pass of gradual typing.
6667
- [x] Added support for classes.
6768
- [x] Added block arguments in method definitions.
6869
- [x] Added numeric literal underscore separators.
6970
- [x] Expanded complex tests/examples coverage.
7071

71-
### v0.5.1
72+
### v0.5.1 (completed 2026-02-07)
7273

7374
- [x] Expanded test suite breadth.
7475
- [x] Landed performance optimizations and refactors.
7576
- [x] Exported `CallBlock()` for embedders.
7677

77-
### v0.6.0
78+
### v0.6.0 (completed 2026-02-11)
7879

7980
- [x] Added runtime memory quota enforcement.
8081
- [x] Enforced strict effects for globals and `require`.
8182
- [x] Isolated `Script.Call` runtime state per invocation.
8283
- [x] Replaced panicking constructors with error-returning APIs.
8384
- [x] Increased CLI/REPL test coverage and added execution benchmarks.
8485

85-
### v0.7.0
86+
### v0.7.0 (completed 2026-02-12)
8687

8788
- [x] Shipped multi-phase string helper expansion.
8889
- [x] Added regex/byte helper coverage and bang-method parity improvements.
8990

90-
### v0.8.0
91+
### v0.8.0 (completed 2026-02-12)
9192

9293
- [x] Expanded Hash built-ins and hash manipulation support.
9394
- [x] Refreshed hash docs and examples.
9495

95-
### v0.9.0
96+
### v0.9.0 (completed 2026-02-12)
9697

9798
- [x] Expanded Array helper surface and enumerable workflows.
9899

99-
### v0.10.0
100+
### v0.10.0 (completed 2026-02-12)
100101

101102
- [x] Made Time and numeric APIs coherent with documented behavior.
102103

103-
### v0.11.0
104+
### v0.11.0 (completed 2026-02-12)
104105

105106
- [x] Improved parse/runtime error feedback and debugging quality.
106107

107-
### v0.12.0
108+
### v0.12.0 (completed 2026-02-13)
108109

109110
- [x] Hardened `require` behavior for safer module composition.
110111
- [x] Improved private helper/module boundary behavior.
111112
- [x] Improved circular dependency diagnostics for modules.
112113

113-
### v0.13.0
114+
### v0.13.0 (completed 2026-02-17)
114115

115116
- [x] Enforced capability contracts at runtime boundaries.
116117
- [x] Added contract validation paths for capability args and returns.
117118
- [x] Improved capability isolation and contract binding behavior.
118119

119120
---
120121

121-
## v0.14.0 - Capability Foundations
122+
## v0.14.0 - Capability Foundations (completed 2026-02-18)
122123

123124
Goal: make host integrations first-class and safe enough for production workflows.
124125

@@ -167,11 +168,11 @@ Goal: make types expressive enough for real workflows while keeping runtime chec
167168

168169
### Type Features
169170

170-
- [ ] Add parametric container types: `array<T>`, `hash<K, V>`.
171-
- [ ] Add union types beyond nil: `A | B`.
172-
- [ ] Add typed object/hash shape syntax for common payload contracts.
173-
- [ ] Add typed block signatures where appropriate.
174-
- [ ] Define type display formatting for readable runtime errors.
171+
- [x] Add parametric container types: `array<T>`, `hash<K, V>`.
172+
- [x] Add union types beyond nil: `A | B`.
173+
- [x] Add typed object/hash shape syntax for common payload contracts.
174+
- [x] Add typed block signatures where appropriate.
175+
- [x] Define type display formatting for readable runtime errors.
175176

176177
### Type Semantics
177178

@@ -182,23 +183,23 @@ Goal: make types expressive enough for real workflows while keeping runtime chec
182183

183184
### Runtime and Parser
184185

185-
- [ ] Extend parser grammar for generic and union type expressions.
186-
- [ ] Extend type resolver and internal type representation.
187-
- [ ] Add runtime validators for composite/union types.
186+
- [x] Extend parser grammar for generic and union type expressions.
187+
- [x] Extend type resolver and internal type representation.
188+
- [x] Add runtime validators for composite/union types.
188189
- [ ] Add contract interop so capability contracts can reuse type validators.
189190

190191
### Testing and Docs
191192

192-
- [ ] Add parser tests for all new type syntax forms.
193-
- [ ] Add runtime tests for nested composite type checks.
194-
- [ ] Add regression tests for existing `any` and nullable behavior.
195-
- [ ] Expand `docs/typing.md` with migration examples.
193+
- [x] Add parser tests for all new type syntax forms.
194+
- [x] Add runtime tests for nested composite type checks.
195+
- [x] Add regression tests for existing `any` and nullable behavior.
196+
- [x] Expand `docs/typing.md` with migration examples.
196197

197198
### v0.15.0 Definition of Done
198199

199-
- [ ] Existing scripts without annotations remain compatible.
200-
- [ ] Type errors include parameter name, expected type, and actual type.
201-
- [ ] Capability contract validation can use the same type primitives.
200+
- [x] Existing scripts without annotations remain compatible.
201+
- [x] Type errors include parameter name, expected type, and actual type.
202+
- [x] Capability contract validation can use the same type primitives.
202203

203204
---
204205

docs/typing.md

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,22 @@ Type names are case-insensitive:
1212
- `array`, `hash`/`object`, `function`
1313
- `any` (no checks)
1414

15+
Parametric containers:
16+
17+
- `array<T>` checks every element against `T`
18+
- `hash<K, V>` checks each key against `K` and each value against `V`
19+
- Example: `array<int>`, `array<int | string>`, `hash<string, int>`
20+
21+
Shape types for object/hash payload contracts:
22+
23+
- `{ id: string, score: int }` requires exactly those keys
24+
- Field values are recursively type-checked
25+
- Extra keys and missing keys fail validation
26+
1527
Nullable: append `?` to allow `nil` (e.g., `string?`, `time?`, `int?`).
1628

29+
Unions: join allowed types with `|` (e.g., `int | string`, `int | nil`).
30+
1731
## Function definitions
1832

1933
Method declarations omit parentheses when there are no args:
@@ -27,6 +41,14 @@ def pick_optional(label: string? = nil) -> string?
2741
label
2842
end
2943
44+
def normalize_id(id: int | string) -> string
45+
id.string
46+
end
47+
48+
def apply_bonus(payload: { id: string, points: int }) -> { id: string, points: int }
49+
{ id: payload[:id], points: payload[:points] + 5 }
50+
end
51+
3052
def nil_result() -> nil
3153
nil
3254
end
@@ -50,12 +72,119 @@ Unknown keyword args and missing required args raise errors.
5072

5173
If a return type is annotated, the returned value is checked. If omitted, no return check is enforced.
5274

75+
## Migration examples
76+
77+
Use a boundary-first strategy: annotate entrypoints that receive external data, then tighten helpers and block callbacks.
78+
79+
### 1) Start with function boundaries
80+
81+
Before:
82+
83+
```vibe
84+
def calculate_total(items)
85+
items.reduce(0) do |acc, item|
86+
acc + item[:amount]
87+
end
88+
end
89+
```
90+
91+
After:
92+
93+
```vibe
94+
def calculate_total(items: array<{ amount: int }>) -> int
95+
items.reduce(0) do |acc: int, item: { amount: int }|
96+
acc + item[:amount]
97+
end
98+
end
99+
```
100+
101+
### 2) Migrate optional values with nullable or unions
102+
103+
Before:
104+
105+
```vibe
106+
def normalize_id(id)
107+
if id == nil
108+
"unknown"
109+
else
110+
id.string
111+
end
112+
end
113+
```
114+
115+
After:
116+
117+
```vibe
118+
def normalize_id(id: int | string | nil) -> string
119+
if id == nil
120+
"unknown"
121+
else
122+
id.string
123+
end
124+
end
125+
```
126+
127+
Use `T?` when the only optional case is `nil`, and use unions when multiple concrete kinds are allowed.
128+
129+
### 3) Convert loose hashes to shape contracts
130+
131+
Before:
132+
133+
```vibe
134+
def reward(payload)
135+
{ id: payload[:id], points: payload[:points] + 10 }
136+
end
137+
```
138+
139+
After:
140+
141+
```vibe
142+
def reward(payload: { id: string, points: int }) -> { id: string, points: int }
143+
{ id: payload[:id], points: payload[:points] + 10 }
144+
end
145+
```
146+
147+
Shapes are strict. Missing or extra keys fail checks.
148+
149+
### 4) Annotate block signatures where callbacks matter
150+
151+
Before:
152+
153+
```vibe
154+
def render_scores(scores)
155+
scores.map do |s|
156+
s + 1
157+
end
158+
end
159+
```
160+
161+
After:
162+
163+
```vibe
164+
def render_scores(scores: array<int>) -> array<int>
165+
scores.map do |s: int|
166+
s + 1
167+
end
168+
end
169+
```
170+
171+
Typed blocks catch callback mismatches at runtime with errors that include parameter name, expected type, and actual type.
172+
173+
### 5) Roll out incrementally
174+
175+
- Add annotations to one high-value path first.
176+
- Keep internal helpers untyped until boundary contracts stabilize.
177+
- Use `any` as a temporary bridge during migration.
178+
- Replace `any` with concrete or shape types once call sites are clean.
179+
- Watch runtime type errors in staging, then tighten signatures further.
180+
53181
## Time and Duration
54182

55183
Duration methods like `ago`/`after` return `Time`. Typed signatures use `time` or `time?` for those values.
56184

57185
## Notes and limitations
58186

59-
- Types are nominal by kind (no generics like `array<int>` yet).
60-
- Nullable via `?` only; unions beyond `nil` are not supported yet.
187+
- Types are nominal by kind.
188+
- Hash keys are runtime strings, so `hash<K, V>` key checks run against string keys.
189+
- Shape types are strict: keys must match exactly.
61190
- Type names are case-insensitive (`Int` == `int`).

vibes/ast.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,18 @@ const (
6161
TypeArray
6262
TypeHash
6363
TypeFunction
64+
TypeShape
65+
TypeUnion
6466
TypeUnknown
6567
)
6668

6769
type TypeExpr struct {
6870
Name string
6971
Kind TypeKind
7072
Nullable bool
73+
TypeArgs []*TypeExpr
74+
Shape map[string]*TypeExpr
75+
Union []*TypeExpr
7176
position Position
7277
}
7378

@@ -272,7 +277,7 @@ func (e *RangeExpr) exprNode() {}
272277
func (e *RangeExpr) Pos() Position { return e.position }
273278

274279
type BlockLiteral struct {
275-
Params []string
280+
Params []Param
276281
Body []Statement
277282
position Position
278283
}

0 commit comments

Comments
 (0)