Skip to content

Schema Validation

A bend schema is a { get, set } lens. Applying it to a stream maps lens.get over every element, turning the stream into a stream of Either: valid messages become { right = ... }, invalid ones { left = ... } carrying blame. .right / .left then split successes from failures.

flowchart TB
  msgs["msgs = st {name age} {name age}"]
  schema["bend.recordAll {name=str; age=int}"]
  func["msgs schema  (stream functor maps lens.get)"]
  either["stream of Either"]
  right[".right  → valid, typed payload"]
  left[".left   → blame {field, got}"]

  msgs --> func
  schema -->|"lens"| func
  func --> either
  either --> right
  either --> left
inherit (dnzl) ned bend;
inherit (ned) st;
# Each message must carry a string `name` and an int `age`.
schema = bend.recordAll { name = bend.str; age = bend.int; };
msgs = st
{ name = "alice"; age = 30; } # valid
{ name = "bob"; age = "oops"; }; # age is not an int
validated = msgs schema; # functor sees a lens → stream of Either
{ ok = validated.right.toList; bad = validated.left.toList; }
# => { ok = [ { right = { name = "alice"; age = 30; }; } ];
# bad = [ { left = { name = { right = "bob"; };
# age = { left = { field = "age"; got = "oops"; }; }; }; } ]; }

Validation is parsing. A valid message comes back as a typed right holding the rebuilt record, so downstream actors receive a clean payload — never a raw, unchecked attrset. An invalid message comes back as a left whose blame names exactly which field failed and what was got.

recordAll collects all field errors at once rather than stopping at the first: a message bad in two fields reports both, each as { left = { field; got; } }.

The whole bend lens layer — recordAll, str, int, nonBlank, optional, and the rest — plugs straight into the stream. Any { get, set } lens works: the functor detects the lens protocol and applies lens.get per element, so schema validation is just another stream stage.

Because recordAll validates every field, one message can surface several blames in a single left:

bad = st { name = 7; age = "oops"; } schema;
bad.left.toList
# => [ { left = { name = { left = { field = "name"; got = 7; }; };
# age = { left = { field = "age"; got = "oops"; }; }; }; } ]
Contribute Community Sponsor