Repository F# setup
open Reified
open Reified.Refinements
open Reified.ResultWalkthrough: Reference Apps
The reference apps exercise CLI, form, JSON, and storage boundaries. Two tiers are runnable:
Reified.ReferenceApp.Intro, plainResult, checks, refined values, and accumulated validation, with no schemas at all. Start there if you are new; this page covers the schema tier.examples/Reified.ReferenceApp.Wire, boundary discipline with the wire tier generated from[<DeriveSchema>]records.
The generated-wire slice
dotnet run --project examples/Reified.ReferenceApp.Wire/Reified.ReferenceApp.Wire.fsproj --nologo
The wire slice generates schemas from ordinary records with constraint attributes:
open Reified.DerivedSchema
type Visibility =
| Private
| Public
[<DeriveSchema>]
type WorkspaceCard =
{ [<Min 1; Max 60>] Name: string
[<Email; SchemaName "owner_email">] OwnerEmail: string
[<Default "private">] Visibility: Visibility
[<Distinct>] Members: string list }
// Called by the generated schema instead of a record literal.
[<SchemaConstructor>]
static member create name (ownerEmail: string) visibility members =
{ Name = name; OwnerEmail = ownerEmail.ToLowerInvariant(); Visibility = visibility; Members = members }
ReifiedDerivedSchemaFsLiveDocsGeneratedPage0_163BCE492C58.VisibilityPrivatePublicReified.DerivedSchema.DeriveSchemaAttributeMarks a plain record for schema derivation: schemagen generates its permissive schema. The advice is to put this on wire DTOs — records that carry no invariants of their own. The attributes in this namespace are inert metadata: they are read from source text at generation time, never by runtime reflection.
FsLiveDocsGeneratedPage0_163BCE492C58.WorkspaceCardReified.DerivedSchema.MinAttributeBounds the natural length of a text, list, or map field from below.
Reified.DerivedSchema.MaxAttributeBounds the natural length of a text, list, or map field from above.
Name: stringstringAn abbreviation for the CLI type . Basic Types
Reified.DerivedSchema.EmailAttributeConstrains a text field to the email format.
Reified.DerivedSchema.SchemaNameAttributeOverrides the external name of one record field or one nullary union case. Without it, field names follow the generation run's naming policy (camelCase by default) and case tags are the camelCased case name.
OwnerEmail: stringReified.DerivedSchema.DefaultAttributeSupplies the field's default when the payload omits it. Not valid on optional fields — absence already parses to None.
Visibility: VisibilityReified.DerivedSchema.DistinctAttributeRequires the elements of a list field to be distinct.
Members: string listlistThe 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.
Reified.DerivedSchema.SchemaConstructorAttributeMarks the static member the derived schema calls to assemble the record, instead of a record literal. Put it on one static member of a [<DeriveSchema>] record that takes the fields in declaration order and returns the record type; use it to normalise values on the way in.
create: string -> string -> Visibility -> string list -> WorkspaceCardname: stringownerEmail: stringvisibility: Visibilitymembers: string listToLowerInvariant: unit -> stringRequires & types
Requires: open Reified.DerivedSchema (packages: Reified.Schema)schemagen writes the sibling workspace.g.fs: the schema pipeline you would have written by hand — schema,
parse, and validate — and, because WorkspaceCardV1/WorkspaceCard follow the version-chain
naming convention, a WorkspaceCard.contract builder that takes your typed v1 → v2 migration. No Fields module is
generated; the wire records are yours, and the generated code refers to them. You write the migration, the strict
domain mapping (TrustedCard rejects an owner listed as a member, a rule the wire deliberately cannot express), and a
head-version write through a compiled codec.
The constraint attributes stay on the wire records, so input is admitted through Contract.parse — which runs
Schema.parse on the selected frozen schema and therefore enforces Min/Max/Email/Distinct. The compiled codec
is used only to write an already-admitted head-version value, not to decode untrusted input. That is the difference
from the permissive-DTO strategy in Schema or Generated DTO: this app wants the
wire rules enforced at the boundary, so it reads with the boundary parser rather than the trusted decoder. Generated
schemas work with JsonSchema.generate and Json.compile.
See Generated version series for the version-series attribute vocabulary and
Build Generation for running generation in your build with Reified.SchemaGen.MSBuild.