Skip to content

Requests And Commands

Chris Michael edited this page Oct 6, 2026 · 1 revision

Pudu

Requests And Commands

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.pudu

Output:

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 with Raised(error); Messaging.lift turns a plain Result into 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 — answers Mismatched rather than running another kind's handler.
  • Mediator.resolve answers 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

Related

Clone this wiki locally