Skip to documentation
SLOP

tiny.mprompt.EffectDefinition

Reference tiny.mprompt EffectDefinition

Defined in effect.

Returns a type for one effect so a caller declares an effect once in Zig, then performs and handles its operations with checked types and no pointer casts.

Called byCallsNo direct callerseffectEffectContinuationprivate sourcelib.mprompt.src.effectPerformFrameprivate sourcelib.mprompt.src.effectSlotprivate sourcelib.mprompt.src.effectcallContinuationHandlerprivate sourcelib.mprompt.src.effectcallTailHandler+9 moreeffectEffectDefinition
Static calls · unresolved targets: 11 · external targets: 1.

Source

Source: lib/mprompt/src/effect.zig:249

zig
/// Returns a type for one effect so a caller declares an effect once in Zig, then performs and/// handles its operations with checked types and no pointer casts. The type is built from a spec/// with a `.name` string and an `.operations` struct literal whose fields are/// `operation(Arg, Result)` values. A spec with more than eight operations is a compile error, and/// so is an `.operations` value of any type other than a struct literal. The type builds the/// effect's name table and one operation tag per operation at compile time, with operation names of/// the form `name/operation`.pub fn EffectDefinition(comptime spec: anytype) type {    const Operations = @TypeOf(spec.operations);    const operations_info = switch (@typeInfo(Operations)) {        .@"struct" => |info| info,        else => @compileError("effect operations must be a struct literal"),    };    const operation_names = operations_info.field_names;    if (operation_names.len > max_operations) {        @compileError("effect definitions support at most eight operations");    }    return struct {        /// An enum with one tag per field of `.operations`, in declaration order. `optag`,        /// `Signature`, `Continuation`, `perform` and `performWithoutValue` take one to pick the        /// operation.        pub const Op: type = std.meta.FieldEnum(Operations);        /// The number of operations in the spec.        pub const operation_count = operation_names.len;        const Self = @This();        const names = blk: {            var result: [operation_count + 2:null]?[*:0]const u8 = undefined;            result[0] = spec.name;            for (operation_names, 0..) |operation_name, index| {                result[index + 1] = std.fmt.comptimePrint("{s}/{s}", .{ spec.name, operation_name });            }            result[operation_count + 1] = null;            break :blk result;        };        const tags = blk: {            var result: [operation_count]Optag = undefined;            for (operation_names, 0..) |_, index| {                result[index] = .{ .effect = rawEffect(), .opidx = index };            }            break :blk result;        };        /// Returns the effect's name table so raw calls that name this effect take it.        pub fn rawEffect() Effect {            return @ptrCast(&names);        }        /// Returns a pointer to `op_id`'s operation tag so raw calls that perform or handle one        /// operation take its tag. The tag's index is the operation's position in `.operations`.        pub fn optag(comptime op_id: Op) *const Optag {            return &tags[operationIndex(op_id)];        }        /// Returns the argument type and result type of `op_id` as the spec declares them.        pub fn Signature(comptime op_id: Op) OperationSignature {            return @field(spec.operations, @tagName(op_id));        }        /// Returns the continuation type that a clause of kind `kind` for `op_id` receives, so a        /// continuation clause names its parameter type with it. A resume of that continuation        /// passes a value of `op_id`'s result type into the body and returns `HandlerResult`.        /// `HandlerResult` must be the `Result` of the `handle` call, and the compiler rejects a        /// mismatch at the clause call site.        pub fn Continuation(comptime op_id: Op, comptime kind: OperationKind, comptime HandlerResult: type) type {            const sig = Signature(op_id);            return EffectContinuation(sig.Result, HandlerResult, kind);        }        /// Performs `op_id` with `arg` and returns the result the clause gives, so code under a        /// handler asks for an operation with a typed argument and gets a typed result. The        /// argument and the result travel in a small record on the performer's stack, and the        /// clause reaches that record by address. Whether the call suspends the body depends on the        /// clause's kind. With no enclosing handler for the effect, the runtime prints        /// `lib/mpeff: unhandled operation:` and the operation's name to standard error. An        /// operation with a non-`void` result then unwraps a null pointer, which panics in safe        /// builds, and an operation with a `void` result returns normally.        pub fn perform(comptime op_id: Op, arg: Signature(op_id).Arg) Signature(op_id).Result {            const sig = Signature(op_id);            const OpFrame = PerformFrame(sig.Arg, sig.Result);            var frame: OpFrame = .{};            writeSlot(sig.Arg, &frame.arg, arg);            return readSlot(sig.Result, performRaw(optag(op_id), &frame));        }        /// Performs `op_id` with no argument, for an operation whose `Arg` is `void`. Any other        /// operation is a compile error.        pub fn performWithoutValue(comptime op_id: Op) Signature(op_id).Result {            const sig = Signature(op_id);            if (sig.Arg != void) {                @compileError("performWithoutValue requires an operation with a void argument");            }            return Self.perform(op_id, {});        }        /// Runs `body(context)` on a new stacklet under a handler for this effect and returns a        /// `Result`, so a caller runs a body under this effect's typed clauses and gets back the        /// body's result, or the result a clause chose. The `context` parameter must be a pointer,        /// and every clause that asks for it receives it. The `clauses` argument is a struct        /// literal with one field per operation, each made by `on` or `forward()`, and a missing        /// field is a compile error. The handler table is built at compile time. The result is the        /// body's return value, the value an `abort` or `never` clause returns, or the value a        /// continuation clause returns. An error union passes through as `Result`.        pub fn handle(            comptime Result: type,            context: anytype,            comptime body: *const fn (@TypeOf(context)) Result,            comptime clauses: anytype,        ) Result {            const Context = @TypeOf(context);            requirePointer(Context, "effect handler context");            const Clauses = @TypeOf(clauses);            const Runner = struct {                const Env = struct {                    context: Context,                    result: Slot(Result) = .{},                };                fn start(arg: ?*anyopaque) callconv(.c) ?*anyopaque {                    const env: *Env = @ptrCast(@alignCast(arg.?));                    writeSlot(Result, &env.result, body(env.context));                    return slotPtr(Result, &env.result);                }                fn table() [max_operations]Operation {                    var entries = @as([max_operations]Operation, @splat(.{ .opkind = .null_op, .optag = null, .opfun = null }));                    inline for (operation_names) |operation_name| {                        if (!hasStructField(Clauses, operation_name)) {                            @compileError("missing handler clause for operation '" ++ operation_name ++ "'");                        }                        const op_id = std.meta.stringToEnum(Op, operation_name).?;                        entries[operationIndex(op_id)] = operationEntry(op_id);                    }                    return entries;                }                fn operationEntry(comptime op_id: Op) Operation {                    const clause = @field(clauses, @tagName(op_id));                    return .{                        .opkind = clause.kind,                        .optag = optag(op_id),                        .opfun = switch (clause.kind) {                            .forward => null,                            .null_op => null,                            else => thunk(op_id),                        },                    };                }                fn thunk(comptime op_id: Op) OpFn {                    return struct {                        fn call(raw_resume: ?*Resume, local: ?*anyopaque, raw_arg: ?*anyopaque) callconv(.c) ?*anyopaque {                            const env: *Env = @ptrCast(@alignCast(local.?));                            const sig = Signature(op_id);                            const OpFrame = PerformFrame(sig.Arg, sig.Result);                            const frame: *OpFrame = @ptrCast(@alignCast(raw_arg.?));                            const op_arg = readSlotValue(sig.Arg, &frame.arg);                            const clause = @field(clauses, @tagName(op_id));                            switch (clause.kind) {                                .tail_noop, .tail => {                                    const op_result = callTailHandler(clause.handler, env.context, op_arg);                                    writeSlot(sig.Result, &frame.result, op_result);                                    return slotPtr(sig.Result, &frame.result);                                },                                .abort, .never => {                                    const result = callTailHandler(clause.handler, env.context, op_arg);                                    writeSlot(Result, &env.result, result);                                    return slotPtr(Result, &env.result);                                },                                .scoped_once, .scoped, .once, .multi => {                                    const continuation: EffectContinuation(sig.Result, Result, clause.kind) = .{                                        .raw_resume = raw_resume.?,                                        .local = local,                                    };                                    const result = callContinuationHandler(clause.handler, continuation, env.context, op_arg);                                    writeSlot(Result, &env.result, result);                                    return slotPtr(Result, &env.result);                                },                                .forward, .null_op => unreachable,                            }                        }                    }.call;                }                const hdef = HandlerDef{                    .effect = rawEffect(),                    .resultfun = null,                    .operations = table(),                };            };            var env: Runner.Env = .{ .context = context };            return readSlot(Result, handleRaw(&Runner.hdef, &env, Runner.start, &env));        }        /// Runs a body that takes no arguments, the way `handle` does, so a caller whose body and        /// clauses need no context skips the pointer. A clause of the `(context, arg)` form        /// receives a pointer to an empty struct.        pub fn handleWithoutContext(            comptime Result: type,            comptime body: *const fn () Result,            comptime clauses: anytype,        ) Result {            const Context = struct {};            const Runner = struct {                fn start(_: *Context) Result {                    return body();                }            };            var context: Context = .{};            return Self.handle(Result, &context, Runner.start, clauses);        }        fn operationIndex(comptime op_id: Op) usize {            inline for (operation_names, 0..) |operation_name, index| {                if (std.mem.eql(u8, operation_name, @tagName(op_id))) return index;            }            unreachable;        }    };}

Source: lib/mprompt/src/root.zig:63

zig
pub const EffectDefinition = effect.EffectDefinition;

Complete call list

14 direct calls.

Audit

Definitions1
Public names2
Members0
Version26.7.0
Revisiondaab053ee433