no :: in func decls

This commit is contained in:
2026-06-30 19:31:26 +02:00
parent ae5af37b85
commit 07c560b750
107 changed files with 580 additions and 520 deletions
+17 -17
View File
@@ -223,7 +223,7 @@
14.5. backward type-demand propagation through call boundaries (DEFERRED)
- a callee's result/return demand flows back through the function body to constrain
the caller's arguments, so `R u32 :: echo(A)` (with `echo :: func(p int) int`)
the caller's arguments, so `R u32 :: echo(A)` (with `echo func(p int) int`)
resolves A to u32 instead of erroring at the call's result coercion
- requires reversing the per-call data flow: a specialization's argument types
(`spec.args`) become outputs to solve, not just inputs — a new back-edge threaded
@@ -981,7 +981,7 @@ Milestone 23 v1 implements named error channels, native sum composition, `return
Functions that can fail declare their error type after `!`:
```
read_file :: func(path []u8) []u8 ! IoError { ... }
read_file func(path []u8) []u8 ! IoError { ... }
```
This reads as: "returns `[]u8` or fails with `IoError`." The space around `!` is idiomatic but not required.
@@ -1035,7 +1035,7 @@ Functions that can fail with multiple error types use `|` to compose a named err
```
ProcessError :: alias IoError | ParseError
process :: func(path []u8) Ast ! ProcessError { ... }
process func(path []u8) Ast ! ProcessError { ... }
```
Parentheses are optional in the composed type and can aid readability:
@@ -1053,7 +1053,7 @@ Inline error types are planned, but not part of milestone 23 v1. Use named enums
Fallible functions use ordinary `return` for both channels. If the returned expression coerces to the success type `T`, the function returns success with channel code `0`. If it coerces to the error type `E`, the function returns the error with that variant's global tag id:
```
parse_section :: func(p: @mut Parser) void ! ParseError {
parse_section func(p: @mut Parser) void ! ParseError {
start_line Line = p.line
p.advance()
@@ -1086,7 +1086,7 @@ The `try` keyword unwraps a successful result or returns early with the error:
```
ProcessError :: alias IoError | ParseError
process :: func(path []u8) Ast ! ProcessError {
process func(path []u8) Ast ! ProcessError {
data :: try read_file(path) # read_file also returns []u8 ! ProcessError in v1
ast :: try parse(data) # parse also returns Ast ! ProcessError in v1
return ast
@@ -1195,7 +1195,7 @@ Brolang provides a **thread-local global heap allocator** that is:
```
import "std/mem/heap"
process :: func(input []u8) u64 {
process func(input []u8) u64 {
# heap used for internal temporary work — does not escape
temp := heap.alloc(u8, size: input.len * 2)
defer heap.free(temp)
@@ -1227,20 +1227,20 @@ import "std/mem"
import "std/mem/heap"
# Allocation escapes via return value — requires allocator
duplicate :: func(input []u8, allocator @mem.Allocator) []u8 {
duplicate func(input []u8, allocator @mem.Allocator) []u8 {
result := allocator.alloc(u8, size: input.len)
mem.copy(result, input)
return result # caller manages this memory
}
# Allocation escapes via mutable parameter — requires allocator
init :: func(obj: @mut MyStruct, allocator: @mem.Allocator) void {
init func(obj: @mut MyStruct, allocator: @mem.Allocator) void {
obj.buffer = allocator.alloc(u8, size: 100)
# caller now knows heap memory was written into obj
}
# No allocation escapes — no allocator needed
process :: func(input: []u8) u64 {
process func(input: []u8) u64 {
temp := heap.alloc(u8, size: input.len)
defer heap.free(temp)
# ... work with temp ...
@@ -1248,11 +1248,11 @@ process :: func(input: []u8) u64 {
}
# No heap allocation at all — no allocator needed
reset :: func(obj: @mut MyStruct) void {
reset func(obj: @mut MyStruct) void {
obj.count = 0
}
main :: func() void {
main func() void {
data := duplicate("hello", heap)
defer heap.free(data)
@@ -1302,7 +1302,7 @@ For specialized needs, you create explicit allocator instances. These are not gl
import "std/mem"
import "std/mem/heap"
process_file :: func(path: []u8, allocator: @mem.Allocator) !Data {
process_file func(path: []u8, allocator: @mem.Allocator) !Data {
# arena manages its own backing memory via heap
arena := mem.Arena.init(heap, capacity: mem.megabytes(1))
defer arena.deinit()
@@ -1333,17 +1333,17 @@ EntitySystem :: struct {
pool: mem.Pool(Entity),
}
init_entities :: func(allocator: @mem.Allocator) EntitySystem {
init_entities func(allocator: @mem.Allocator) EntitySystem {
return EntitySystem{
pool = mem.Pool(Entity).init(allocator, capacity: 10_000),
}
}
spawn :: func(sys: @mut EntitySystem) @Entity {
spawn func(sys: @mut EntitySystem) @Entity {
return sys.pool.alloc() # O(1), no fragmentation
}
despawn :: func(sys: @mut EntitySystem, entity: @Entity) void {
despawn func(sys: @mut EntitySystem, entity: @Entity) void {
sys.pool.free(entity) # returned to pool for reuse
}
```
@@ -1356,7 +1356,7 @@ As described in the escaping allocation rule, when a function heap-allocates mem
import "std/mem"
# Function that uses caller's allocator
parse :: func(input: []u8, allocator: @mem.Allocator) !ParseResult {
parse func(input: []u8, allocator: @mem.Allocator) !ParseResult {
buffer := allocator.alloc(u8, size: input.len)
defer allocator.free(buffer)
@@ -1367,7 +1367,7 @@ parse :: func(input: []u8, allocator: @mem.Allocator) !ParseResult {
}
# Caller decides which allocator to use
main :: func() void {
main func() void {
# use an arena for this parsing work
arena := mem.Arena.init(heap, capacity: mem.kilobytes(64))
defer arena.deinit()