type Port = u16 where self >= 1 && self <= 65535
error Invalid { message : String }
domain ParseError = Invalid
function parsePort (s : String ) -> Port effects [fail < ParseError > ] {
const trimmed = text . trim (s)
const n = check number . parse_u16 (trimmed) context { op : "parse_u16" }
if n < 1 || n > 65535 {
return err Invalid { message : "port out of range" }
}
return ok Port (n)
}
error NotFound { path : Path }
error Denied { path : Path }
domain IoError = NotFound | Denied
function readAll (path : Path , cap fs : Fs ) -> Bytes effects [fs, fail < IoError > ] {
const file = check fs . open (path)
return check fs . readAll (file)
}
function processWithArena (input : View < u8 > ) -> Output effects [alloc] {
const arena = region . arena ( 1 << 20 )
defer arena . dispose ()
const temp = arena :: Vec . new < u8 > ()
// ... process into temp ...
return result . clone () // clone to caller's region
}
function handleRequest (req : Request , cap fs : Fs ) -> Response
effects [fs, alloc, fail < AppError > ]
{
const arena = region . arena ( 1 << 20 )
defer arena . dispose ()
const file = arena :: check openFile (path, fs)
const data = arena :: check file . readAll ()
const result = transform (data)
return result . clone ()
}
type Eq < T > = { eq : ( T , T ) -> Bool }
type Hash < T > = { hash : ( T ) -> u64 }
type Show < T > = { show : ( T ) -> String }
type UserId = { id : u64 }
impl Eq < UserId > {
eq : function (a : UserId , b : UserId ) -> Bool { return a . id == b . id },
}
impl Hash < UserId > {
hash : function (u : UserId ) -> u64 { return u . id },
}
impl Show < UserId > {
show : function (u : UserId ) -> String { return fmt "User({u.id})" },
}
function dedupe < T > (items : View < T > ) -> Vec < T >
where T impl Eq & Hash
effects [alloc]
{
// compiler resolves T.Eq and T.Hash
}
const unique = dedupe (users)
error Timeout { url : Url , ms : u64 }
error Network { message : String }
domain FetchError = Timeout | Network
function fetch (url : Url , cap net : Net , cap clock : Clock ) -> Response
effects [net, time, fail < FetchError > ]
{
scope timeout ( Duration . seconds ( 30 ), clock) {
const sock = check net . connect (url . host, url . port)
return check request (sock, url)
} on timeout {
return err Timeout { url : url, ms : 30000 }
}
}
function processAll (items : View < Item > , cap fs : Fs , cap clock : Clock ) -> Vec < Result >
effects [fs, time, alloc, suspend, fail < ProcessError > ]
{
scope timeout ( Duration . seconds ( 30 ), clock) {
scope parallel {
items . map ( function (item : Item ) -> Result {
return check processItem (item, fs)
})
}
} on timeout {
return err ProcessError . Timeout {}
}
}
comptime function crc16Table (poly : u16 ) -> Array < u16 , 256 > {
var table : Array < u16 , 256 > = [ 0 ; 256 ]
var i : u32 = 0
while i < 256 {
var crc : u16 = u16 (i) << 8
var j : u32 = 0
while j < 8 {
if (crc & 0x8000 ) != 0 {
crc = (crc << 1 ) ^ poly
} else {
crc = crc << 1
}
j = j + 1
}
table[i] = crc
i = i + 1
}
return table
}
const CRC16_TABLE = comptime crc16Table ( 0x1021 )
function crc16 (data : View < u8 > ) -> u16 {
var crc : u16 = 0xFFFF
for byte in data {
const idx = ((crc >> 8 ) ^ u16 (byte)) & 0xFF
crc = (crc << 8 ) ^ CRC16_TABLE [ usize (idx)]
}
return crc
}
function handleRequest (req : Request , cap fs : Fs , cap net : Net ) -> Response
effects [fs, net, fail < AppError > ]
{
with context { request_id : req . id, user_id : req . user } in {
const config = check loadConfig (fs)
const data = check fetchExternal (req . url, net)
// all errors within this block carry request_id and user_id
return ok processData (data, config)
}
}
// covariant: can only output T
type Producer < out T > = { get : () -> T }
// contravariant: can only input T
type Consumer < in T > = { accept : ( T ) -> Unit }
// Producer<Cat> is assignable to Producer<Animal>
function printAnimal (p : Producer < Animal > , cap io : Io ) -> Unit effects [io] {
io . println (p . get () . name)
}
const catProducer : Producer < Cat > = { get : function () -> Cat { return myCat } }
printAnimal (catProducer, io) // OK: Cat is Animal
type Unwrap < T > = if T is Result < infer U , infer E > then U else T
type UnwrappedConfig = Unwrap < Result < Config , Error >> // Config
type ElementType < A > = if A is Array < infer T , infer N > then T else Never
type StringElement = ElementType < Array < String , 10 >> // String
function map < T , U , E > (arr : View < T > , f : ( T ) -> U effects [fail < E > , ... ]) -> Vec < U >
effects [alloc, fail < E > , ... ]
{
const result = builder . new < U > (region . current ())
for item in arr {
builder . push (result, f (item))
}
return builder . finish (result)
}
// caller's effects include alloc + whatever the passed function has
function processAll (items : View < Item > , cap fs : Fs ) -> Vec < Output >
effects [alloc, fs, fail < IoError > ]
{
return map (items, function (item : Item ) -> Output effects [fs, fail < IoError > ] {
return check fs . process (item)
})
}