Repository F# setup
open Reified
open Reified.Refinements
open Reified.Result
open Reified.Schema.JsonSignup Form Tutorial
This page shows how to declare a schema, parse form input, and redisplay boundary errors without constructing invalid models.
This tutorial parses a signup form into a trusted model. If any field fails, no model is constructed and the form can be redisplayed with the user's original input and per-field errors.
Declare The Model And Schema
The schema declares each field once: external name, getter, and constraints.
open Reified
open Reified.SchemaDSL
type Signup = { Email: string; Age: int }
let signupSchema =
schema<Signup> {
field _.Email {
constraints [ email; maxLength 254 ]
}
field _.Age {
constrain (atLeast 13)
}
construct (fun email age -> { Email = email; Age = age })
}schema<Signup> anchors the model type. The closing constructor must match every field in declaration order, so
missing or mistyped arguments fail at construct.
Adapt The structured data
Form posts are name/value pairs:
let raw =
Data.ofNameValues
[ "email", "not-an-email"
"age", "12" ]Parse
let parsed = Schema.parseRetainingInput signupSchema rawparsed is a RetainedParseResult<Signup, SchemaError>. On success parsed.Result is Ok signup and every constraint
already holds. Here both fields fail, so no Signup exists anywhere:
parsed.IsValid // false
parsed.ErrorsFor "email" // [ SchemaError.InvalidFormat "email" ]
parsed.ErrorsFor "age" // [ SchemaError.OutOfRange ... ]Redisplay The Form
The original input is retained on the parsed value, addressed by the same paths:
Data.redisplayPath "email" parsed.Input // "not-an-email", exactly as typed
Data.redisplayPath "age" parsed.Input // "12"A form template needs only parsed.Input and parsed.ErrorsFor — there is no half-valid model to guard against.
Use SchemaError.render for field-level messages or RetainedParseResult.renderErrors parsed for a summary list.
Use The Trusted Model
match parsed.Result with
| Ok signup -> register signup // constraints already hold; no re-checking downstream
| Error _ -> renderForm parsedSignup here is a public record, so the guarantee belongs to the successful parse result, not to the type — other
code can still write a Signup literal that skips the schema. That is the right trade for a boundary form model.
When a value's construction history is uncertain, Schema.check signupSchema value runs the same constraints over an
already assembled value; when an invariant must hold for every value of the type, use a private representation with a
smart constructor. Construction Guarantees covers the full division.
Next
- Nested Models And Collections for models inside models.
- Redisplay And Field Errors for the full redisplay guide.
- Construction Guarantees for which claims need a private type rather than a schema.