Skip to main content

10  Flags, Ramifications and Error

Edva unifies booleans, optional values, fallible statuses, and result types into a single two-slot algebraic construct: the ramification (ram). Every ramification has a positive (first) slot and a negative (second) slot.

10.1 The four ramification layouts and ABI

Layout Type Syntax Positive (1st Slot) Negative (2nd Slot) Runtime ABI & Niche Lowering Role
RamNN <> <+> (unit) <-> (unit) Unboxed i1 (1 = <+>, 0 = <->) Boolean Flag (built-in, no #type needed)
RamPN < T | > <+ val +> (T) <--> (unit) Null-pointer niche (ptr, null = <-->) for String/Record; otherwise { i1, T } Option (value or absence)
RamNP < | E > <++> (unit) <- err -> (E) Null-pointer niche (ptr, null = <++>) for String/Record/Error; otherwise { i1, E } Fallible (void success or error)
RamPP < T | E > <+ val -> (T) <- err +> (E) Unboxed register aggregate { i1, i64 } (i1 = polarity tag, 0 heap wrapper cost) Result (value or error)

10.1.1 Constructor delimiter matrix

All payload constructors close with +> or -> (never bare >), ensuring relational comparisons inside payloads (<+ a > b ->) are unambiguous:

Target Shape Positive Slot Constructor Negative Slot Constructor Coercion Rule
<> (RamNN) <+> <-> Never implicitly coerces to <++> or <-->
< T | > (RamPN) <+ val +> <--> Implicit | <--> synthesized in non-exhaustive expression choices
< | E > (RamNP) <++> <- err -> Implicit | <++> synthesized in non-exhaustive expression choices
< T | E > (RamPP) <+ val -> <- err +> Both slots carry typed payloads
#use io

#type MaybeInt < Int | >
#type Status < | String >
#type Outcome < Int | String >

flag = <+>
opt = MaybeInt(<+ 42 +>)
stat = Status(<++>)
res = Outcome(<+ 100 ->)

io::out $ (flag, opt | _ | 0, stat | "ok" | _, res!!)

10.2 Operations on flags and ramifications

Operation Syntax Input Type Result Type Semantics & Diagnostics
Logical NOT !f <> <> Inverts <+> \(\leftrightarrow\) <-> (E3008 on non-flag types)
Logical AND / OR a && b, a || b <> or compatible ram Same Short-circuiting conjunction / disjunction
Flag extraction <>(r) / !<>(r) Any ram r <> Discards payloads and returns <+> if r is positive (!<>(r) negates)
Positional choice r | pos | neg Any ram r Unified branch type 1st | matches positive slot; 2nd | matches negative slot (_ or x => binds payload)
Skip-first choice r !| neg_body < | E > / <> Unified type Sugar for r | (void) | neg_body (e.g. arr(i) = v !| _ panics on OOB write)
Unwrap or abort r!! < T | E > or < T | > T Extracts positive payload T or aborts on negative slot (E3091 on <|E> / <>)
Unwrap or propagate r--! < T | E >, < T | >, < | E >, <> T or () Extracts positive slot or early-returns the negative ram from the enclosing function
Monadic bind r $>> cont < T | E > or < T | > Ram returned by cont Calls cont(val) on positive slot; propagates negative slot unchanged (E3091 if cont non-ram)
Early return sugar c |-> c |+> c |--> c |++> c: <> () (returns from fn) Early-returns <->, <+>, <-->, or <++> when c is <+>
#use io

parse_pos: Int => < Int | String >
parse_pos = n =>
   n !> 0 | --| <- "non-positive" +>
   <+ n * 10 ->

double_pos: Int => < Int | String >
double_pos = n =>
   v = parse_pos(n)--!
   <+ v * 2 ->

io::out $ double_pos(3) | _ | -1     // 60
io::out $ double_pos(-1) | _ | -1    // -1

10.3 The Error type and I/O conventions

Error is the built-in structured diagnostic record used in < T | Error > and < | Error >:

Field / Form Type / Syntax Semantics
err.code Int Numeric error code (POSIX errno, HTTP status, or application code)
err.msg String Human-readable diagnostic message
err.file, err.line, err.col String, Int, Int Source location automatically captured by Error(code, msg) at the call site
err.context (Int, Int) Auxiliary context tuple (defaults to (0, 0))
Error(code, msg) Constructor Captures caller file, line, col automatically
r | _ | _ on < T | Error > Fatal negative _ Extracting a negative Error slot with bare _ aborts execution with E4011 (like r!!); bind a named parameter (| err => ...) to handle recoverably
EOF vs Failure in I/O < String | > vs < T | Error > Line/stream readers (in::line) return < String | > (<--> = normal EOF); filesystem/network calls (fs::read_file) return < String | Error >
#use io

#type ReadResult < String | Error >

load_item: Int => ReadResult
load_item = id =>
   id < 0
      | ReadResult(<- Error(404, "item not found") +>)
      | ReadResult(<+ "payload" ->)

res = load_item(-1)
msg = res
   | data => data
   | err => "E${err.code}: ${err.msg}"

io::out $ msg