Functional Error Handling with Metrics (pkg/functional)
A fluent, functional-style Go API for monadic error handling, safe map operations, and telemetry-integrated execution with automatic metrics capture via the coordinator pattern.
Overview
Go's idiomatic (value, error) convention frequently leads to verbose, repetitive boilerplate and deeply nested conditional branches. The pkg/functional package provides:
Result[T]Monad: A generic, type-safe container encapsulating either a successful value (T) or anerror, inspired by Rust'sResult<T, E>.- Functional Combinators: Composable operators (
Map,MapErr,AndThen,OrElse,OrElseGet) that transform and chain computations without manual error branching. - Telemetry-Integrated Execution: Higher-order functions (
Apply,Do,Get) that execute operations while automatically recording execution duration, status, and error metadata into the ZQK coordinator event pipeline. - Nil-Safe Map Utilities: Helper functions for pointer maps, default lookups, and key/value slice extractions.
- Fluent Conditional Execution: Re-exported
Whenbuilder for expressive conditional pipelines.
Core Types & API Contract
1. Result[T any]
The primary generic type representing either an operation's successful payload or an error:
type Result[T any] struct {
value T // Encapsulated success value (unexported)
err error // Encapsulated error state (unexported)
}
Constructors
| Constructor | Signature | Description |
|---|---|---|
Ok |
func Ok[T any](value T) Result[T] |
Wraps a successful value into a Result[T] with err = nil. |
Err |
func Err[T any](err error) Result[T] |
Wraps an error into a Result[T] with zero-value T. |
From |
func From[T any](value T, err error) Result[T] |
Lifts a standard Go (T, error) tuple directly into Result[T]. |
Inspection & Unwrapping Methods
| Method | Signature | Description |
|---|---|---|
IsOk() |
func (r Result[T]) IsOk() bool |
Returns true if the result represents a success (err == nil). |
IsErr() |
func (r Result[T]) IsErr() bool |
Returns true if the result represents an error (err != nil). |
Value() |
func (r Result[T]) Value() (T, error) |
Unpacks the Result[T] back into a standard Go (T, error) tuple. |
Unwrap() |
func (r Result[T]) Unwrap() T |
Returns the inner value. Panics with error details if IsErr() == true. |
UnwrapOr() |
func (r Result[T]) UnwrapOr(defaultValue T) T |
Returns the inner value if successful; otherwise returns defaultValue. |
UnwrapOrElse() |
func (r Result[T]) UnwrapOrElse(fn func(error) T) T |
Returns the inner value if successful; otherwise computes fallback value via fn(err). |
2. Functional Combinators
Combinators allow transforming and chaining Result values declaratively:
| Function | Signature | Semantic Meaning |
|---|---|---|
Map |
func Map[T, U any](r Result[T], fn func(T) U) Result[U] |
Applies fn to the inner value if IsOk(). If IsErr(), propagates the error. |
MapErr |
func MapErr[T any](r Result[T], fn func(error) error) Result[T] |
Applies fn to the error if IsErr(). If IsOk(), leaves value untouched. |
AndThen |
func AndThen[T, U any](r Result[T], fn func(T) Result[U]) Result[U] |
Chains an operation that returns another Result[U] (monadic flatMap). |
OrElse |
func OrElse[T any](r Result[T], alternative Result[T]) Result[T] |
Returns r if IsOk(); otherwise returns alternative. |
OrElseGet |
func OrElseGet[T any](r Result[T], fn func(error) Result[T]) Result[T] |
Returns r if IsOk(); otherwise evaluates fn(err) to produce a fallback Result[T]. |
3. Telemetry Configuration Types
Apply, Do, and Get accept functional options to configure metrics and event routing:
// ApplyConfig holds runtime configuration for telemetry-enabled operations
type ApplyConfig struct {
coordinator coordination.EventCoordinator // Coordinator destination for metrics
operationType string // Telemetry label identifying the operation
}
// ApplyOption modifies ApplyConfig
type ApplyOption func(*ApplyConfig)
Available Options
WithCoordinator(c coordination.EventCoordinator) ApplyOption: Sets an explicit event coordinator.WithOperationType(opType string) ApplyOption: Labels the emitted metric event (e.g."storage_init","query_exec").WithoutMetrics() ApplyOption: Disables metrics collection (useful in lightweight inner loops or unit tests).
Basic Usage
Constructing and Inspecting Results
package main
import (
"errors"
"fmt"
"github.com/zqk-os/zqk/pkg/functional"
)
func main() {
// 1. Success result
res1 := functional.Ok(42)
fmt.Println(res1.IsOk()) // true
fmt.Println(res1.Unwrap()) // 42
// 2. Error result
res2 := functional.Err[int](errors.New("disk full"))
fmt.Println(res2.IsErr()) // true
fmt.Println(res2.UnwrapOr(0)) // 0
// 3. From standard Go call
value, err := computeSomething()
res3 := functional.From(value, err)
// 4. Safe fallback with error inspect
finalVal := res3.UnwrapOrElse(func(err error) int {
fmt.Printf("Computation failed: %v, using default\n", err)
return -1
})
_ = finalVal
}
func computeSomething() (int, error) {
return 100, nil
}
Transforming and Chaining Results
// Map value: double the integer if Ok
doubled := functional.Map(res1, func(x int) int {
return x * 2
})
// Map error: annotate failure context
betterErr := functional.MapErr(res2, func(err error) error {
return fmt.Errorf("storage layer failure: %w", err)
})
// AndThen: Monadic chain returning a different Result type
stringified := functional.AndThen(res1, func(x int) functional.Result[string] {
if x < 0 {
return functional.Err[string](errors.New("negative value"))
}
return functional.Ok(fmt.Sprintf("val=%d", x))
})
Metrics Integration & Telemetry Execution
pkg/functional bridges execution with the ZQK coordination spinal cord. Any operation wrapped via Apply, Do, or Get automatically measures duration, handles error states, and emits an event to the metrics channel.
Caller
│
▼
functional.Apply(ctx, target, fn, WithOperationType("storage_init"))
│
├── 1. time.Now()
├── 2. fn(target) ──> (U, error)
├── 3. duration = time.Since(start)
├── 4. coordinator.Emit(ctx, eventCtx) ──> Metrics / Event Stream
│
▼
Result[U] (Ok or Err)
1. Apply Family (Transforms Input T to Output U)
Standard Apply
storageProvider := functional.Apply(
ctx,
projectRoot,
func(root string) (storage.ObjectStorageProvider, error) {
return storage.NewFileObjectStorage(root)
},
functional.WithOperationType("storage_init"),
).UnwrapOr(nil)
Apply with Fallback (ApplyOrElse)
Executes the primary function, falling back to an alternative if it fails:
result := functional.ApplyOrElse(
ctx,
target,
primaryFunction,
func(err error) (Output, error) {
logger.Warn("Primary failed, attempting fallback", "err", err)
return fallbackFunction(target)
},
functional.WithOperationType("resilient_operation"),
)
Chained Apply (ApplyAndThen)
Pipelines two functions sequentially with automatic propagation:
result := functional.ApplyAndThen(
ctx,
rawInput,
parseStage, // func(Raw) (Parsed, error)
enrichStage, // func(Parsed) (Enriched, error)
functional.WithOperationType("pipeline_stage"),
)
2. Do Family (Side Effects Returning Only error)
For operations that do not yield values, only errors:
// Execute side-effect operation
err := functional.Do(
ctx,
task,
func(t Task) error {
return t.Execute()
},
functional.WithOperationType("task_execution"),
)
// Do with fallback
err = functional.DoOrElse(
ctx,
task,
primaryAction,
fallbackAction,
functional.WithOperationType("task_recovery"),
)
// Chained Do: step1 produces U, step2 consumes U and returns error
err = functional.DoAndThen(
ctx,
input,
func(in Input) (Intermediate, error) {
return buildIntermediate(in)
},
func(inter Intermediate) error {
return commitIntermediate(inter)
},
functional.WithOperationType("commit_pipeline"),
)
3. Get Family (Value Producers Without Input Target)
For zero-argument producer functions:
// Fetch value with metrics
res := functional.Get(
ctx,
fetchRemoteConfig,
functional.WithOperationType("fetch_config"),
)
// Get with static fallback
val := functional.GetOrElse(
ctx,
fetchRemoteConfig,
defaultConfig,
functional.WithOperationType("fetch_config"),
)
// Get with computed fallback
res = functional.GetOrElseGet(
ctx,
fetchRemoteConfig,
func(err error) (Config, error) {
return loadLocalCacheConfig()
},
functional.WithOperationType("fetch_config"),
)
Nil-Safe Map Utilities (maps.go)
pkg/functional provides nil-tolerant and generic map helper routines:
import "github.com/zqk-os/zqk/pkg/functional"
// 1. Pointer Map Lookup (nil-safe, returns nil if absent or val is nil)
var ptrMap map[string]*Session
session := functional.MapGetPtr(ptrMap, "session-123") // nil, does not panic
// 2. Pointer Map Membership Check (true only if present AND pointer != nil)
hasValidSession := functional.MapHasPtr(ptrMap, "session-123")
// 3. Map Get With Fallback
portMap := map[string]int{"http": 8080}
grpcPort := functional.MapGetOrDefault(portMap, "grpc", 9090) // 9090
// 4. Map Keys Extraction
keys := functional.MapKeys(portMap) // []string{"http"}
// 5. Map Values Extraction
vals := functional.MapValues(portMap) // []int{8080}
Fluent Conditional Execution (when.go)
Re-exports When from pkg/when to enable clean, declarative conditional execution without multi-tier if statements:
import "github.com/zqk-os/zqk/pkg/functional"
functional.When(func() bool {
return isProduction && featureFlagEnabled
}).Then(func() {
startHighFrequencyMonitor()
})
Benefits & Architectural Alignment
- Elimination of Defensive Boilerplate: Replaces 5-line
if err != nilnesting with clean, one-line functional transformations. - First-Class Observability: Built directly into the
Apply,Do, andGetdispatch mechanisms, ensuring uniform latency and error tracking. - Rust-Grade Type Safety: Generics ensure compile-time type verification with zero runtime reflection overhead.
- Resilient Failure Recovery: Seamless fallback chains with
ApplyOrElse,OrElseGet, andUnwrapOrElse. - Zero Magic Literals: Perfectly integrates with domain constants and
coordination.EventContext.