sess

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2026 License: MIT Imports: 4 Imported by: 0

README

Go Reference Go Report Card Coverage Status

English | 简体中文 | Español | 日本語 | Français

sess

Session-typed communication protocols via algebraic effects on kont.

Overview

sess provides typed, bidirectional protocols composed of six operations, each dispatched as an algebraic effect on a lock-free endpoint pair.

  • Dual-world API: Cont (closure-based) and Expr (frame-based, zero-allocation hot paths)
  • Stepping: Evaluate effects one at a time for proactor and event-loop integration
  • iox Non-blocking Algebra: Enforces a strict progress model where operations natively yield iox.ErrWouldBlock at computational boundaries, allowing proactor event loops (e.g., io_uring) to seamlessly multiplex execution without thread-blocking

Installation

go get code.hybscloud.com/sess

Requires Go 1.26+.

Session Operations

Operation Effect Suspends?
Send[T] Send a value iox.ErrWouldBlock
Recv[T] Receive a value iox.ErrWouldBlock
Close End the session Never
SelectL Choose the left branch iox.ErrWouldBlock
SelectR Choose the right branch iox.ErrWouldBlock
Offer Wait for the peer's choice iox.ErrWouldBlock

Delegate an endpoint by sending it; accept delegation by receiving it.

Usage

Send and Receive
client := sess.SendThen(42, sess.CloseDone("ok"))
server := sess.RecvBind(func(n int) kont.Eff[string] {
    return sess.CloseDone(fmt.Sprintf("got %d", n))
})
a, b := sess.Run(client, server) // "ok", "got 42"

Expr equivalent: ExprSendThen, ExprRecvBind, ExprCloseDone, RunExpr.

Branching
client := sess.SelectLThen(sess.SendThen(1, sess.CloseDone("left")))
server := sess.OfferBranch(
    func() kont.Eff[string] {
        return sess.RecvBind(func(n int) kont.Eff[string] {
            return sess.CloseDone(fmt.Sprintf("left %d", n))
        })
    },
    func() kont.Eff[string] { return sess.CloseDone("right") },
)
a, b := sess.Run(client, server)
Recursive Protocols
counter := sess.Loop(0, func(i int) kont.Eff[kont.Either[int, string]] {
    if i >= 3 {
        return sess.CloseDone(kont.Right[int, string]("done"))
    }
    return sess.SendThen(i, kont.Pure(kont.Left[int, string](i+1)))
})
Stepping

For real proactor event loops (e.g., io_uring), sess provides a Step and Advance mechanism. Unlike the Run and Exec helpers—which use iox.Backoff to synchronously wait for progress—the stepping API is the true non-blocking algebra that explicitly yields iox.ErrWouldBlock to the caller, allowing the event loop to seamlessly multiplex execution without thread-blocking.

ep, _ := sess.New()
protocol := sess.ExprSendThen(42, sess.ExprCloseDone[struct{}](struct{}{}))
_, susp := sess.Step[struct{}](protocol)
// In a proactor event loop (e.g., io_uring), yield on boundary:
_, nextSusp, err := sess.Advance(ep, susp)
if err != nil {
    return susp // yield to event loop, reschedule when ready
}
susp = nextSusp
Error Handling
clientResult, serverResult := sess.RunError[string, string, string](client, server)
// Either[string, string]: Right on success, Left on Throw

Execution Model

Function Use case
Run / RunExpr Run both sides on one goroutine — creates an endpoint pair internally
Exec / ExecExpr Run one side on a pre-created endpoint
Step + Advance Evaluate one effect at a time, for external event loops

Cont vs Expr: Cont is closure-based and straightforward to compose. Expr is frame-based with amortized zero-allocation, suited for hot paths.

API

Category Cont Expr
Constructors SendThen, RecvBind, CloseDone, SelectLThen, SelectRThen, OfferBranch ExprSendThen, ExprRecvBind, ExprCloseDone, ExprSelectLThen, ExprSelectRThen, ExprOfferBranch
Recursion Loop ExprLoop
Execution Exec, Run ExecExpr, RunExpr
Error execution ExecError, RunError ExecErrorExpr, RunErrorExpr
Stepping Step, Advance, StepError, AdvanceError
Bridge Reify (Cont→Expr), Reflect (Expr→Cont)
Transport New(*Endpoint, *Endpoint)

Dependencies

License

MIT — see LICENSE.

©2026 Hayabusa Cloud Co., Ltd.

Documentation

Overview

Package sess provides session-typed communication protocols via algebraic effects on code.hybscloud.com/kont.

Protocols are composed from typed operations, each dispatched as an algebraic effect on a session endpoint.

Design

Non-Blocking Semantics

Session operations are strictly non-blocking. Operations directly query the underlying transport and return code.hybscloud.com/iox.ErrWouldBlock when backpressure limits are reached:

The stepping API (Step, Advance) preserves this boundary. Blocking helpers (Exec, Run) use code.hybscloud.com/iox.Backoff to wait past the boundary.

Session Operations

Six operations define the protocol vocabulary. Each implements DispatchSession with non-blocking semantics:

  • Send: Send a typed value to the peer
  • Recv: Receive a typed value from the peer
  • Close: Signal session termination (never blocks)
  • SelectL: Choose the left branch
  • SelectR: Choose the right branch
  • Offer: Wait for the peer's branch choice

Endpoint delegation is Send/Recv of *Endpoint.

Cont-World API

Fused constructors compose operations with continuation sequencing: SendThen, RecvBind, CloseDone, SelectLThen, SelectRThen, OfferBranch.

Expr-World API

Defunctionalized constructors mirror the Cont-world API with pooled frames for amortized zero-allocation evaluation: ExprSendThen, ExprRecvBind, ExprCloseDone, ExprSelectLThen, ExprSelectRThen, ExprOfferBranch.

Recursive Protocols

Loop and ExprLoop express recursive protocols via tailRecM. The step function returns Left(nextState) to continue or Right(result) to finish.

Execution

Blocking helpers wait past code.hybscloud.com/iox.ErrWouldBlock via adaptive backoff:

  • Exec, ExecExpr: Run a protocol on a pre-created endpoint
  • Run, RunExpr: Create a pair and interleave both sides cooperatively

Stepping / Proactor Integration

One-effect-at-a-time evaluation for external runtimes:

  • Step: Evaluate a protocol until the first effect suspension
  • Advance: Dispatch the suspended operation (non-blocking)

Unconsumed suspensions from code.hybscloud.com/iox.ErrWouldBlock are retryable. code.hybscloud.com/kont.Suspension.Op returns the concrete operation for protocol-aware scheduling.

Error Handling

Composed session+error dispatch: session operations are non-blocking, error operations (code.hybscloud.com/kont.ThrowError, code.hybscloud.com/kont.CatchError) short-circuit. Results are code.hybscloud.com/kont.Either — Right on success, Left on Throw.

Cont ↔ Expr Bridge

  • Reify: Cont-world → Expr-world
  • Reflect: Expr-world → Cont-world

Transport

New creates a connected Endpoint pair using four bounded SPSC queues (two data, two choice) from code.hybscloud.com/lfq.

Serial

Each session pair receives a monotonically increasing Serial. Both endpoints share the same serial.

Example (Stepping)

epA, _ := sess.New()
protocol := sess.ExprSendThen(42, sess.ExprCloseDone[struct{}](struct{}{}))
_, susp := sess.Step[struct{}](protocol)
for susp != nil {
	var err error
	_, susp, err = sess.Advance(epA, susp)
	if err != nil {
		continue // retry on ErrWouldBlock
	}
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Advance

func Advance[R any](ep *Endpoint, susp *kont.Suspension[R]) (R, *kont.Suspension[R], error)

Advance dispatches the suspended session operation on the endpoint. DispatchSession is non-blocking: returns iox.ErrWouldBlock when the bounded SPSC queue cannot make progress (the I/O boundary).

On success (nil error), the suspension is consumed and the protocol advances to the next effect or completion. On iox.ErrWouldBlock, the suspension is unconsumed and may be retried after the peer makes progress.

func AdvanceError

func AdvanceError[E, R any](ep *Endpoint, susp *kont.Suspension[kont.Either[E, R]]) (kont.Either[E, R], *kont.Suspension[kont.Either[E, R]], error)

AdvanceError dispatches the suspended operation on the endpoint. Session ops are non-blocking (ErrWouldBlock). Error ops are eager: Throw discards the suspension and returns Left.

func CloseDone

func CloseDone[A any](a A) kont.Eff[A]

CloseDone closes the session and returns a. Fuses Perform(Close{}) + Then + Pure.

func Exec

func Exec[R any](ep *Endpoint, protocol kont.Eff[R]) R

Exec runs a Cont-world session protocol on a pre-created endpoint. Blocks on iox.ErrWouldBlock via adaptive backoff (iox.Backoff), without spawning goroutines or creating channels.

func ExecError

func ExecError[E, R any](ep *Endpoint, protocol kont.Eff[R]) kont.Either[E, R]

ExecError runs a Cont-world session protocol with error handling on a pre-created endpoint. Returns Either[E, R] — Right on success, Left on Throw. Blocks on iox.ErrWouldBlock via adaptive backoff (iox.Backoff), without spawning goroutines or creating channels.

func ExecErrorExpr

func ExecErrorExpr[E, R any](ep *Endpoint, protocol kont.Expr[R]) kont.Either[E, R]

ExecErrorExpr runs an Expr-world session protocol with error handling on a pre-created endpoint. Returns Either[E, R] — Right on success, Left on Throw. Blocks on iox.ErrWouldBlock via adaptive backoff (iox.Backoff), without spawning goroutines or creating channels.

func ExecExpr

func ExecExpr[R any](ep *Endpoint, protocol kont.Expr[R]) R

ExecExpr runs an Expr-world session protocol on a pre-created endpoint. Blocks on iox.ErrWouldBlock via adaptive backoff (iox.Backoff), without spawning goroutines or creating channels.

func ExprCloseDone

func ExprCloseDone[A any](a A) kont.Expr[A]

ExprCloseDone closes the session and returns a. Fuses ExprPerform(Close{}) + ExprThen + ExprReturn.

func ExprLoop

func ExprLoop[S, A any](initial S, step func(S) kont.Expr[kont.Either[S, A]]) kont.Expr[A]

ExprLoop runs a recursive session protocol (Expr-world). step returns Left(nextState) to continue or Right(result) to finish. Fuses ExprBind inline to avoid the type-erasing wrapper closure.

func ExprOfferBranch

func ExprOfferBranch[A any](onLeft func() kont.Expr[A], onRight func() kont.Expr[A]) kont.Expr[A]

ExprOfferBranch waits for the peer's choice and calls onLeft or onRight. Fuses ExprPerform(Offer{}) + ExprBind + Either branch.

func ExprRecvBind

func ExprRecvBind[T, B any](f func(T) kont.Expr[B]) kont.Expr[B]

ExprRecvBind receives a value and passes it to f. Fuses ExprPerform(Recv[T]{}) + ExprBind.

func ExprSelectLThen

func ExprSelectLThen[B any](next kont.Expr[B]) kont.Expr[B]

ExprSelectLThen selects the left branch and continues with next. Fuses ExprPerform(SelectL{}) + ExprThen.

func ExprSelectRThen

func ExprSelectRThen[B any](next kont.Expr[B]) kont.Expr[B]

ExprSelectRThen selects the right branch and continues with next. Fuses ExprPerform(SelectR{}) + ExprThen.

func ExprSendThen

func ExprSendThen[T, B any](v T, next kont.Expr[B]) kont.Expr[B]

ExprSendThen sends a value and then continues with next. Fuses ExprPerform(Send[T]{Value: v}) + ExprThen.

func Loop

func Loop[S, A any](initial S, step func(S) kont.Eff[kont.Either[S, A]]) kont.Eff[A]

Loop runs a recursive session protocol (Cont-world). step returns Left(nextState) to continue or Right(result) to finish.

func New

func New() (*Endpoint, *Endpoint)

New creates a connected pair of session endpoints. Internal transport uses bounded lock-free SPSC queues: two for data (A→B, B→A), two for branch choice (A→B, B→A), and a shared atomic counter for close signaling.

Session operations are non-blocking: DispatchSession returns iox.ErrWouldBlock when the peer has not yet produced or consumed.

func OfferBranch

func OfferBranch[A any](onLeft func() kont.Eff[A], onRight func() kont.Eff[A]) kont.Eff[A]

OfferBranch waits for the peer's choice and calls onLeft or onRight. Fuses Perform(Offer{}) + Bind + Either branch.

func RecvBind

func RecvBind[T, B any](f func(T) kont.Eff[B]) kont.Eff[B]

RecvBind receives a value and passes it to f. Fuses Perform(Recv[T]{}) + Bind.

func Reflect

func Reflect[A any](m kont.Expr[A]) kont.Eff[A]

Reflect converts an Expr-world session protocol to Cont-world. The resulting Eff can be evaluated with Exec or Run.

func Reify

func Reify[A any](m kont.Eff[A]) kont.Expr[A]

Reify converts a Cont-world session protocol to Expr-world. The resulting Expr can be evaluated with ExecExpr, RunExpr, or stepped with Step and Advance.

func Run

func Run[A, B any](a kont.Eff[A], b kont.Eff[B]) (A, B)

Run creates a session pair, runs both Cont-world protocols, and returns both results. Interleaves execution of both sides on the calling goroutine using adaptive backoff (iox.Backoff) when neither side can make progress. Does not spawn goroutines or create channels.

func RunError

func RunError[E, A, B any](a kont.Eff[A], b kont.Eff[B]) (kont.Either[E, A], kont.Either[E, B])

RunError creates a session pair, runs both Cont-world protocols with error handling, and returns both results as Either values. Interleaves execution of both sides on the calling goroutine using adaptive backoff (iox.Backoff). Does not spawn goroutines or create channels.

func RunErrorExpr

func RunErrorExpr[E, A, B any](a kont.Expr[A], b kont.Expr[B]) (kont.Either[E, A], kont.Either[E, B])

RunErrorExpr creates a session pair, runs both Expr-world protocols with error handling, and returns both results as Either values. Interleaves execution of both sides on the calling goroutine using adaptive backoff (iox.Backoff). Does not spawn goroutines or create channels.

func RunExpr

func RunExpr[A, B any](a kont.Expr[A], b kont.Expr[B]) (A, B)

RunExpr creates a session pair, runs both Expr-world protocols, and returns both results. Interleaves execution of both sides on the calling goroutine using adaptive backoff (iox.Backoff) when neither side can make progress. Does not spawn goroutines or create channels.

func SelectLThen

func SelectLThen[B any](next kont.Eff[B]) kont.Eff[B]

SelectLThen selects the left branch and continues with next. Fuses Perform(SelectL{}) + Then.

func SelectRThen

func SelectRThen[B any](next kont.Eff[B]) kont.Eff[B]

SelectRThen selects the right branch and continues with next. Fuses Perform(SelectR{}) + Then.

func SendThen

func SendThen[T, B any](v T, next kont.Eff[B]) kont.Eff[B]

SendThen sends a value and then continues with next. Fuses Perform(Send[T]{Value: v}) + Then.

func Step

func Step[R any](protocol kont.Expr[R]) (R, *kont.Suspension[R])

Step evaluates a session protocol until the first effect suspension. Returns (result, nil) on completion, or (zero, suspension) if pending.

func StepError

func StepError[E, R any](protocol kont.Expr[R]) (kont.Either[E, R], *kont.Suspension[kont.Either[E, R]])

StepError evaluates a session protocol with error support until the first effect suspension. Returns (Either[E, R], nil) on completion or error, or (zero, suspension) if pending.

Types

type Close

type Close struct {
	kont.Phantom[struct{}]
}

Close is the effect operation for closing the session. Perform(Close{}) signals session termination.

func (Close) DispatchSession

func (Close) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles Close on the session transport. Atomically increments the shared close counter. Never blocks.

type Endpoint

type Endpoint struct {
	// contains filtered or unexported fields
}

Endpoint represents one side of a session-typed channel pair. Transport is backed by bounded lock-free SPSC queues from lfq.

func (*Endpoint) Serial

func (ep *Endpoint) Serial() Serial

Serial returns the serial number assigned to this endpoint's session.

type Offer

type Offer struct {
	kont.Phantom[kont.Either[struct{}, struct{}]]
}

Offer is the effect operation for receiving a branch choice from the peer. Perform(Offer{}) receives the peer's Left or Right selection.

func (Offer) DispatchSession

func (Offer) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles Offer on the session transport. Non-blocking: returns iox.ErrWouldBlock if the choice queue is empty. true → Left (peer selected left), false → Right (peer selected right).

type Recv

type Recv[T any] struct {
	kont.Phantom[T]
}

Recv is the effect operation for receiving a value of type T. Perform(Recv[T]{}) receives a typed value from the peer.

func (Recv[T]) DispatchSession

func (Recv[T]) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles Recv on the session transport. Non-blocking: returns iox.ErrWouldBlock if the bounded SPSC queue is empty.

type SelectL

type SelectL struct {
	kont.Phantom[struct{}]
}

SelectL is the effect operation for choosing the left branch. Perform(SelectL{}) signals the left choice to the peer.

func (SelectL) DispatchSession

func (SelectL) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles SelectL on the session transport. Non-blocking: returns iox.ErrWouldBlock if the choice queue is full.

type SelectR

type SelectR struct {
	kont.Phantom[struct{}]
}

SelectR is the effect operation for choosing the right branch. Perform(SelectR{}) signals the right choice to the peer.

func (SelectR) DispatchSession

func (SelectR) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles SelectR on the session transport. Non-blocking: returns iox.ErrWouldBlock if the choice queue is full.

type Send

type Send[T any] struct {
	kont.Phantom[struct{}]
	Value T
}

Send is the effect operation for sending a value of type T. Perform(Send[T]{Value: v}) sends v to the peer endpoint.

func (Send[T]) DispatchSession

func (s Send[T]) DispatchSession(ctx *sessionContext) (kont.Resumed, error)

DispatchSession handles Send on the session transport. Non-blocking: returns iox.ErrWouldBlock if the bounded SPSC queue is full.

type Serial

type Serial = uint32

Serial is a monotonically increasing session identifier. Each call to New assigns the next serial value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL