Repository F# setup
open Reified
open Reified.Refinements
open Reified.ResultSchema Integration
Schema describes structured input: fields, wire representations, path-aware failures, accumulation, and reconstruction. A refinement supplies the value-level step that turns an already-decoded underlying value into an invariant-carrying domain value. Schema can apply that refinement at a field boundary and report its failures at the field's path.
Reified.Refinements has no Schema dependency. Domain types can define refinements without choosing a wire format; an
application that uses Reified.Schema decides where those refinements participate in structured decoding and encoding.
The Schema quickstart introduces fields, record construction, and path-aware diagnostics. Then read Refined Values in Schema for canonical field schemas, raw-value constraints, explicit refinement, and schema-local restrictions.
Refine a primitive schema
open Reified.Refinements
open Reified
let nameSchema : Schema<NonBlankString> =
Schema.text
|> Schema.refine NonBlankString.refinement
ReifiedRefinementsnameSchema: Schema<NonBlankString>Reified.Schema`1Describes a typed value's portable structure and construction for schema interpreters. A schema records shape and construction metadata without tying that metadata to input parsing, diagnostics, validation, codecs, UI generation, or workflow execution. Primitive, collection, optional, union, refined, and record declarations all produce Schema<'value>. Record declarations use the schema<'value> { } computation expression. Each field may contain withSchema, constrain, constraints, refine, and validate operations before the declaration finishes with construct or constructResult.
Reified.Refinements.NonBlankStringA string that is not null, empty, or whitespace.
Reified.SchemaConstruction, composition, parsing, and checking for universal schemas.
text: Schema<string>Describes text input.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
refine: Refinement<'raw,'value> -> Schema<'raw> -> Schema<'value>Maps a raw schema through a reusable bidirectional refinement. The smart constructor runs during parsing. Inspection supplies the raw representation during checking and encoding.
Reified.Refinements.NonBlankStringModuleOperations over text known to carry non-whitespace content.
refinement: Refinement<string,NonBlankString>Parsing checks the underlying string, constructs NonBlankString, and reports failures at the schema path. Encoding
and checking project through NonBlankString.Value.
A numeric range is a constraint rather than a refined type, so it goes on the primitive:
field _.Quantity { constrain (Constraint.greaterThan 0) }Schema.constrain is available for a standalone value schema too, but inside a field block
the schema is inferred from the field's type and each constraint sits on its own line.
For an application type:
open Reified
open Reified.Refinements
type ContactEmail = private ContactEmail of string
module ContactEmail =
let value (ContactEmail value) = value
let refinement = Refinement.define Constraint.email ContactEmail value
let emailSchema : Schema<ContactEmail> =
Schema.text
|> Schema.refine ContactEmail.refinement
ReifiedRefinementsFsLiveDocsGeneratedPage32_6BB268B4E740.ContactEmailContactEmailstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage32_6BB268B4E740.ContactEmailModulevalue: ContactEmail -> string_arg1: ContactEmailvalue: stringrefinement: Refinement<string,ContactEmail>Reified.Refinements.RefinementCreates and applies reusable refinement definitions.
define: Constraint<'underlying> -> ('underlying -> 'refined) -> ('refined -> 'underlying) -> Refinement<'underlying,'refined>Defines a refinement from one constraint, a constructor, and the reverse projection. Compose several rules with Constraint.all before defining, and reach for Constraint.custom when the rule is an arbitrary predicate. Both produce an ordinary constraint, so there is no separate plural or check-taking constructor. type RetryCount = RetryCount of int let retryCount = Refinement.define (Constraint.between 0 10) RetryCount (fun (RetryCount value) -> value)
Reified.ConstraintModuleCreates, executes, composes, and inspects constraints.
email: Constraint<string>Requires text to match Reified's pragmatic email shape, ^[^@]+@[^@]+$. let contact : Constraint<string> = Constraint.email
emailSchema: Schema<ContactEmail>Reified.Schema`1Describes a typed value's portable structure and construction for schema interpreters. A schema records shape and construction metadata without tying that metadata to input parsing, diagnostics, validation, codecs, UI generation, or workflow execution. Primitive, collection, optional, union, refined, and record declarations all produce Schema<'value>. Record declarations use the schema<'value> { } computation expression. Each field may contain withSchema, constrain, constraints, refine, and validate operations before the declaration finishes with construct or constructResult.
Reified.SchemaConstruction, composition, parsing, and checking for universal schemas.
text: Schema<string>Describes text input.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
refine: Refinement<'raw,'value> -> Schema<'raw> -> Schema<'value>Maps a raw schema through a reusable bidirectional refinement. The smart constructor runs during parsing. Inspection supplies the raw representation during checking and encoding.
A field block receives the refinement explicitly:
open Reified
open Reified.SchemaDSL
open Reified.Refinements
type ContactEmail = private ContactEmail of string
module ContactEmail =
let value (ContactEmail value) = value
let refinement = Refinement.define Constraint.email ContactEmail value
type Signup = { Email: ContactEmail; Age: int }
module Signup =
let create email age = { Email = email; Age = age }
let signupSchema =
schema<Signup> {
field _.Email {
withSchema Schema.text
refine ContactEmail.refinement
}
field _.Age
construct Signup.create
}
ReifiedReified.SchemaDSLThe concise schema-definition vocabulary: the record computation expression, its field and constructor forms, and the collection-schema operations. Optional and opt-in, in the same shape as Reified.DataDSL and Reified.ConstraintDSL: open Reified.Schema for Schema, then open Reified.SchemaDSL for this vocabulary. There is no constraint catalogue here. One Constraint vocabulary serves direct checking, refinement, and Schema, so a field block reaches for Constraint.email or an opened Reified.ConstraintDSL exactly as standalone code does. Boundary supply is Schema-owned and stays here as mustSupply and mayOmit.
RefinementsFsLiveDocsGeneratedPage33_6BB268B4E740.ContactEmailContactEmailstringAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage33_6BB268B4E740.ContactEmailModulevalue: ContactEmail -> string_arg1: ContactEmailvalue: stringrefinement: Refinement<string,ContactEmail>Reified.Refinements.RefinementCreates and applies reusable refinement definitions.
define: Constraint<'underlying> -> ('underlying -> 'refined) -> ('refined -> 'underlying) -> Refinement<'underlying,'refined>Defines a refinement from one constraint, a constructor, and the reverse projection. Compose several rules with Constraint.all before defining, and reach for Constraint.custom when the rule is an arbitrary predicate. Both produce an ordinary constraint, so there is no separate plural or check-taking constructor. type RetryCount = RetryCount of int let retryCount = Refinement.define (Constraint.between 0 10) RetryCount (fun (RetryCount value) -> value)
Reified.ConstraintModuleCreates, executes, composes, and inspects constraints.
email: Constraint<string>Requires text to match Reified's pragmatic email shape, ^[^@]+@[^@]+$. let contact : Constraint<string> = Constraint.email
FsLiveDocsGeneratedPage33_6BB268B4E740.SignupEmail: ContactEmailAge: intintAn abbreviation for the CLI type . Basic Types
FsLiveDocsGeneratedPage33_6BB268B4E740.SignupModulecreate: ContactEmail -> int -> Signupemail: ContactEmailage: intsignupSchema: Schema<Signup>schema: SchemaCeBuilder.SchemaBuilder<'model>Record-schema computation expression.
``.ctor``: Quotations.Expr<(Signup -> ContactEmail)> -> field<Signup,ContactEmail>Declares a field, deriving its camel-cased wire name from the property getter. type Signup = { Email: string } field (fun (s: Signup) -> s.Email) // wire name "email"
``.ctor``: Quotations.Expr<('model -> 'target)> -> field<'model,'target>Declares a field, deriving its camel-cased wire name from the property getter. type Signup = { Email: string } field (fun (s: Signup) -> s.Email) // wire name "email"
_arg1: SignupWithSchema: SchemaFieldSteps.FieldInitial<Signup,ContactEmail> * Schema<'current> -> SchemaFieldSteps.FieldWorking<Signup,ContactEmail,'current>Supplies the schema transformed by the remaining operations in this field block.
Reified.SchemaConstruction, composition, parsing, and checking for universal schemas.
text: Schema<string>Describes text input.
Refine: SchemaFieldSteps.FieldWorking<Signup,ContactEmail,'raw> * Refinement<'raw,ContactEmail> -> SchemaFieldSteps.FieldWorking<Signup,ContactEmail,ContactEmail>Refines the current raw schema with an explicit refinement.
``.ctor``: Quotations.Expr<(Signup -> int)> -> field<Signup,int>Declares a field, deriving its camel-cased wire name from the property getter. type Signup = { Email: string } field (fun (s: Signup) -> s.Email) // wire name "email"
_arg2: Signupconstruct: 'constructor -> SchemaCeBuilder.ConstructorStep<'model,'constructor>Closes a record schema with a total constructor.
Choose the operation by meaning
Schema.refinerefinement schemaconstructs an invariant-carrying destination and retains refinement metadata.Schema.convertforward backward schemaperforms a total projected mapping.Schema.tryConvertforward backward schemaperforms a fallible projected mapping returningSchemaError list.Schema.admitcreate project draftSchemaconstructs a domain model from a structured draft while preserving fields.
type Cents = Cents of int
let centsSchema : Schema<Cents> =
Schema.int
|> Schema.convert Cents (fun (Cents cents) -> cents)
FsLiveDocsGeneratedPage31_6BB268B4E740.CentsCentsintAn abbreviation for the CLI type . Basic Types
centsSchema: Schema<Cents>Reified.Schema`1Describes a typed value's portable structure and construction for schema interpreters. A schema records shape and construction metadata without tying that metadata to input parsing, diagnostics, validation, codecs, UI generation, or workflow execution. Primitive, collection, optional, union, refined, and record declarations all produce Schema<'value>. Record declarations use the schema<'value> { } computation expression. Each field may contain withSchema, constrain, constraints, refine, and validate operations before the declaration finishes with construct or constructResult.
Reified.SchemaConstruction, composition, parsing, and checking for universal schemas.
int: Schema<int>Describes a 32-bit integer.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
convert: ('a -> 'b) -> ('b -> 'a) -> Schema<'a> -> Schema<'b>Maps a schema through a total, reversible domain conversion.
_arg1: Centscents: intconvert requires a total forward mapping, because it constructs the value with no error channel. A narrowing
backward mapping such as decimal → int truncates fractional values and can throw on out-of-range input, so it
belongs behind Schema.tryConvert with an explicit error, not in a total convert. The example above stays
reversible in both directions.
A draft stays public and freely constructible; only the aggregate it admits into enforces the invariant, see
Construction Guarantees for the full rationale. DateTimeOffset, not
DateOnly, for the same reason Build A Private Aggregate gives: a
canonical schema for every target. The draft's own schema is declared before the private Booking below it
reuses the same field names, declaring it after would make _.Start resolve against Booking instead:
open Reified
open Reified.SchemaDSL
open System
type BookingDraft = { Start: DateTimeOffset; End: DateTimeOffset }
let bookingDraftSchema : Schema<BookingDraft> =
schema<BookingDraft> {
field _.Start
field _.End
construct (fun start finish -> { Start = start; End = finish })
}
type Booking =
private
{ Start: DateTimeOffset
End: DateTimeOffset }
module Booking =
let create (draft: BookingDraft) : Result<Booking, SchemaError list> =
if draft.Start <= draft.End then Ok { Start = draft.Start; End = draft.End }
else Error [ SchemaError.Custom("end-before-start", Some "End must not be before start.") ]
let toDraft (booking: Booking) : BookingDraft =
{ Start = booking.Start; End = booking.End }
let bookingSchema : Schema<Booking> =
bookingDraftSchema
|> Schema.admit Booking.create Booking.toDraft
ReifiedReified.SchemaDSLThe concise schema-definition vocabulary: the record computation expression, its field and constructor forms, and the collection-schema operations. Optional and opt-in, in the same shape as Reified.DataDSL and Reified.ConstraintDSL: open Reified.Schema for Schema, then open Reified.SchemaDSL for this vocabulary. There is no constraint catalogue here. One Constraint vocabulary serves direct checking, refinement, and Schema, so a field block reaches for Constraint.email or an opened Reified.ConstraintDSL exactly as standalone code does. Boundary supply is Schema-owned and stays here as mustSupply and mayOmit.
SystemFsLiveDocsGeneratedPage34_6BB268B4E740.BookingDraftStart: DateTimeOffsetSystem.DateTimeOffsetRepresents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC).
End: DateTimeOffsetbookingDraftSchema: Schema<BookingDraft>Reified.Schema`1Describes a typed value's portable structure and construction for schema interpreters. A schema records shape and construction metadata without tying that metadata to input parsing, diagnostics, validation, codecs, UI generation, or workflow execution. Primitive, collection, optional, union, refined, and record declarations all produce Schema<'value>. Record declarations use the schema<'value> { } computation expression. Each field may contain withSchema, constrain, constraints, refine, and validate operations before the declaration finishes with construct or constructResult.
schema: SchemaCeBuilder.SchemaBuilder<'model>Record-schema computation expression.
``.ctor``: Quotations.Expr<(BookingDraft -> DateTimeOffset)> -> field<BookingDraft,DateTimeOffset>Declares a field, deriving its camel-cased wire name from the property getter. type Signup = { Email: string } field (fun (s: Signup) -> s.Email) // wire name "email"
_arg1: BookingDraft_arg2: BookingDraftconstruct: 'constructor -> SchemaCeBuilder.ConstructorStep<'model,'constructor>Closes a record schema with a total constructor.
start: DateTimeOffsetfinish: DateTimeOffsetFsLiveDocsGeneratedPage34_6BB268B4E740.BookingFsLiveDocsGeneratedPage34_6BB268B4E740.BookingModulecreate: BookingDraft -> Result<Booking,SchemaError list>draft: BookingDraftMicrosoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
Reified.SchemaErrorSchema input, checking, and contextual rule failures attached to diagnostics paths. The parse/check axis is the organising split. Parsing cases mean the input could not be read as the declared type at all; Violation means it was read and then failed its constraint. Constraint failures are never lowered into a parse-shaped case, because a lowering that discards the atom forces consumers back to reconstructing constraint identity from strings.
listThe type of immutable singly-linked lists. See the module for further operations related to lists. Use the constructors [] and :: (infix) to create values of this type, or the notation [1; 2; 3]. Use the values in the List module to manipulate values of this type, or pattern match against the values directly. See also F# Language Guide - Lists.
(<=): 'T -> 'T -> boolStructural less-than-or-equal comparison The first parameter. The second parameter. The result of the comparison. 5 <= 1 // Evaluates to false 5 <= 5 // Evaluates to true [1; 5] <= [1; 6] // Evaluates to true
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
CustomA Schema-owned intrinsic check failed.
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
toDraft: Booking -> BookingDraftbooking: BookingbookingSchema: Schema<Booking>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Reified.SchemaConstruction, composition, parsing, and checking for universal schemas.
admit: ('draft -> Result<'domain,SchemaError list>) -> ('domain -> 'draft) -> Schema<'draft> -> Schema<'domain>Admits a permissive draft model schema into a trusted domain schema through an admission function and a projection, preserving fields, wire names, constraints, and metadata.
For the complete progression from a raw field schema to a canonical refined field, continue to Refined Values in Schema.