Scopes

io.eezo.db.Scopes
object Scopes

The scopes: the whole database API's entry points. DESIGN §8.2.

import io.eezo.db.Scopes.* and write transact { ... }; no Database is ever named.

Attributes

Source
Scopes.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Scopes.type

Members list

Type members

Classlikes

final class Attempt[E <: Throwable]

The second half of attempt, split off only so the caller names E and A is inferred.

The second half of attempt, split off only so the caller names E and A is inferred.

The failure is judged by a TypeTest rather than a ClassTag because a union such as SQLException | TimeoutException has no single class: its ClassTag is the common superclass, Exception, and would quietly own every defect the caller never named.

The type stays public, so a caller can hold one and call apply, but the constructor does not: a public one would let new Attempt[Throwable] or new Attempt[Nothing] build the same unguarded savepoint that attempt's summonFrom refuses to hand out. publicInBinary is there only because attempt is inline and expands new Attempt[E] at the call site: Scala refuses to inline a private constructor without it, so attempt itself would not compile.

Attributes

Source
Scopes.scala
Supertypes
class Object
trait Matchable
class Any

Value members

Concrete methods

inline def attempt[E <: Throwable](using TypeTest[Throwable, E]): Attempt[E]

A sub unit that may fail and be recovered, on a savepoint. DESIGN §8.12.

A sub unit that may fail and be recovered, on a savepoint. DESIGN §8.12.

The savepoint owns only the failure its caller names, because recovering is a claim that the transaction is still fine to use. A duplicate key is something the caller expected and knows how to answer; a defect, or a scope guard's refusal, is something nobody inside the transaction planned for, and turning it into a value would let the request carry on as if it had. So anything else thrown inside still ends the request, with the enclosing transact rolling back. Naming a broad type such as RuntimeException brings that swallowing back, guard refusals included, which is why the choice is left in plain sight at the call site.

attempt[E] returns an Attempt rather than taking the body itself because Scala 3 cannot apply one type argument and infer the other: with [E, A] on one method every caller would have to spell out A as well. A bare attempt { ... } would infer E as Nothing and own no failure at all, while reading as if it recovered; naming Throwable would own every failure. Neither is a choice the caller made, so both are refused at compile time.

Never implicit in nesting: past 64 subtransactions PGPROC's cached subxid array overflows and every visibility check hits the subtrans SLRU, which is a cliff for a savepoint per iteration.

Attributes

Source
Scopes.scala
def detached[A](body: Tx ?=> A): A

A transaction that commits independently of the caller's.

A transaction that commits independently of the caller's.

Takes the body rather than merely clearing the marker, because a lexically nested transact would be rejected by summonFrom — so detached opens the scope itself.

Attributes

Source
Scopes.scala
inline def read[A](inline body: DB ?=> A): A

Opens a read scope.

Opens a read scope.

Attributes

Source
Scopes.scala
inline def transact[A](inline body: Tx ?=> A): A

Opens a transaction.

Opens a transaction.

inline only far enough to run summonFrom; the body goes to Run.tx, which is where the capture contract lives. See Run for why that split is load-bearing rather than tidy.

Attributes

Source
Scopes.scala