Repository navigation
Requests And Commands
Chris Michael edited this page Oct 6, 2026
·
1 revision
A request kind answers a value; a command kind (Request.command) answers (). Each takes
exactly one handler, and the build refuses a kind with two, or with behaviors but no handler,
reporting every problem at once. A handler's error type is yours — here a sum the caller can match:
module Ex02Requests
import Std.Io as Io
import PuduLangMediator.Context as Context
import PuduLangMediator.Mediator as Mediator
import PuduLangMediator as Messaging
import PuduLangMediator.Registration as Registration
import PuduLangMediator.Request as Request
type AccountError = NotFound(Int) | Locked
export fn main() -> Int {
let findAccount: Request.Kind[Int, Str, AccountError] = Request.kind("accounts.find")
let lockAccount: Request.Kind[Int, (), AccountError] = Request.command("accounts.lock")
let refused = Mediator.build([
Registration.handler(&findAccount, |id: Int, _context: Context.Context| Ok("account " + show(id))),
Registration.handler(&findAccount, |id: Int, _context: Context.Context| Ok("again " + show(id))),
Registration.behavior(&lockAccount, fn(id: Int, context: Context.Context, next: Request.Next[(), AccountError]) -> Messaging.Outcome[(), AccountError] { next(context) })
])
if let Err(invalid) = refused { let _said = Io.writeLine(Mediator.explain(&invalid)) }
let mediator = match Mediator.build([
Registration.handler(&findAccount, fn(id: Int, _context: Context.Context) -> Messaging.Outcome[Str, AccountError] {
if id == 1 { Ok("ada") } else { Messaging.raise(NotFound(id)) }
}),
Registration.handler(&lockAccount, fn(id: Int, _context: Context.Context) -> Messaging.Outcome[(), AccountError] {
Messaging.lift(if id == 1 { Ok(()) } else { Err(Locked) })
})
]) {
case Ok(built) => built
case Err(invalid) => panic(Mediator.explain(&invalid))
}
let found = Mediator.send(&mediator, &findAccount, 1)
let missing = Mediator.send(&mediator, &findAccount, 7)
let locked = Mediator.send(&mediator, &lockAccount, 1)
let twin: Request.Kind[Int, Str, AccountError] = Request.kind("accounts.find")
let mismatched = Mediator.send(&mediator, &twin, 1)
let find = match Mediator.resolve(&mediator, &findAccount) {
case Ok(invoke) => invoke
case Err(failure) => panic(Messaging.describe(&failure))
}
let hot = find(1, Context.create())
for line in [Messaging.summarize(&found), Messaging.summarize(&missing), Messaging.summarize(&locked), Messaging.summarize(&mismatched), Messaging.summarize(&hot)] {
let _said = Io.writeLine(line)
}
if Messaging.raised(&(match missing { case Err(failure) => failure case Ok(_) => Messaging.Refused("") })) == Some(NotFound(7)) { 0 } else { 1 }
}
Check it and run it:
pudu check src/Ex02Requests.pudu
pudu run src/Ex02Requests.puduOutput:
The mediator is invalid: 'accounts.find' has 2 handlers; a request or stream takes exactly one; 'accounts.lock' has behaviors or processors but no handler
Ok("ada")
The handler failed: NotFound(7)
Ok(())
'accounts.find' is registered for another kind of the same name.
Ok("ada")
-
Messaging.raise(error)fails withRaised(error);Messaging.liftturns a plainResultinto an outcome;Messaging.raised(&failure)reads your error back out. - Sending a kind nobody registered answers
Unhandled. Sending a twin — a kind made separately under a registered name — answersMismatchedrather than running another kind's handler. -
Mediator.resolveanswers the composed pipeline itself. A hot path resolves once and calls it directly, skipping the lookup on every message.
| Failure | Meaning |
|---|---|
Raised(E) |
the handler's own error |
Unhandled(name) |
no handler is registered for the kind |
Mismatched(name) |
another kind holds the name |
Refused(reason) |
a behavior or processor refused the message |
Invalid(violations) |
validation failed |
Cancelled(reason) |
the context's token fired |
Crashed(reason) |
a handler's thread stopped |
Aggregate(failures) |
several handlers of one publication failed |