Repository F# setup
open Reified
open Reified.Refinements
open Reified.Result
open Reified.Schema.JsonWorking with violations
Constraint.check returns Error violation when a value fails. A Violation is structured diagnostic data; render
it for a person, or keep it structured when application code needs to classify or translate the failure.
Render an English message
Use Violation.render at the edge where the failure becomes text:
open Reified
let retryCount : Constraint<int> =
Constraint.between 0 10
33
|> Constraint.check retryCount
|> Result.mapError Violation.render
// Error "expected a value between 0 and 10, but was 33"
ReifiedretryCount: Constraint<int>Reified.Constraint`1A reusable description of valid values, coupled to the closures that execute it. One constraint value serves direct checking, refined-value admission, Schema, documentation, and export. There is no separate check type: check is the operation, Constraint is the noun. Both closures are retained deliberately. They are not duplicates of one rule: test over a conjunction may stop at the first failing child, while check must run every child to accumulate. Interpreted atoms and custom predicates therefore have a Boolean path that does no violation work, and combinators preserve that property when every child has it. A customWith constraint supplies only a violation-returning callback, so its test runs that callback and discards the error. The description is never interpreted during execution. Closures are composed once, at construction.
intAn abbreviation for the CLI type . Basic Types
Reified.ConstraintModuleCreates, executes, composes, and inspects constraints.
between: 'value -> 'value -> Constraint<'value>Requires a value inside the supplied inclusive bounds. let retryCount : Constraint<int> = Constraint.between 0 10
(|>): '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
check: Constraint<'value> -> 'value -> Result<unit,Violation>Runs a constraint, returning why the value failed. let retryCount = Constraint.between 0 10 42 |> Constraint.check retryCount |> Result.mapError Violation.render
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
mapError: ('a -> 'b) -> Result<'c,'a> -> Result<'c,'b>Maps the error value of a result.
Reified.ViolationModuleInspects, traverses, and renders violations.
render: Violation -> stringRenders a violation as an English sentence fragment with no trailing punctuation, keeping conjunction and alternative groups distinct. Violation.render (Atomic (Expected (PresenceAtom Present, None))) // "value must be present"
When handling the result directly:
match 33 |> Constraint.check retryCount with
| Ok () -> printfn "valid"
| Error violation -> printfn "%s" (Violation.render violation)
(|>): '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.ConstraintModuleCreates, executes, composes, and inspects constraints.
check: Constraint<'value> -> 'value -> Result<unit,Violation>Runs a constraint, returning why the value failed. let retryCount = Constraint.between 0 10 42 |> Constraint.check retryCount |> Result.mapError Violation.render
retryCount: Constraint<int>OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
printfn: Printf.TextWriterFormat<'T> -> 'TPrint to stdout using the given format, and add a newline. The formatter. The formatted result. See Printf.printfn (link: ) for examples.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
violation: ViolationReified.ViolationModuleInspects, traverses, and renders violations.
render: Violation -> stringRenders a violation as an English sentence fragment with no trailing punctuation, keeping conjunction and alternative groups distinct. Violation.render (Atomic (Expected (PresenceAtom Present, None))) // "value must be present"
render returns an English sentence fragment with no trailing punctuation. Add punctuation or a field label in the
presentation layer that owns the complete message.
Keep the violation in application errors
Do not turn every constraint failure into a string immediately. An application error can retain the Violation and
choose how to present it later:
type SignupError =
| InvalidName of Violation
let name : Constraint<string> =
Constraint.all [ Constraint.present; Constraint.lengthBetween 2 40 ]
let validateName value =
value
|> Constraint.guard name
|> Result.mapError InvalidName
04-constraints_35-violations.md_page.SignupErrorInvalidNameReified.ViolationWhy a value failed its constraint. A diagnostic contract, not an application error union. Domain code maps a whole violation once with Result.mapError; Schema adds the path at which it occurred. Violations are plain comparable data. No closure and no constraint description is reachable from one, so structural equality holds and a violation can be retained and compared long after the constraint that produced it went out of scope. There is no promised wire format. Reified-produced groups are never empty and never unary: a single failing child is returned directly rather than wrapped. The first * rest shape encodes non-emptiness only; non-unarity is a normalization invariant.
name: Constraint<string>Reified.Constraint`1A reusable description of valid values, coupled to the closures that execute it. One constraint value serves direct checking, refined-value admission, Schema, documentation, and export. There is no separate check type: check is the operation, Constraint is the noun. Both closures are retained deliberately. They are not duplicates of one rule: test over a conjunction may stop at the first failing child, while check must run every child to accumulate. Interpreted atoms and custom predicates therefore have a Boolean path that does no violation work, and combinators preserve that property when every child has it. A customWith constraint supplies only a violation-returning callback, so its test runs that callback and discards the error. The description is never interpreted during execution. Closures are composed once, at construction.
stringAn abbreviation for the CLI type . Basic Types
Reified.ConstraintModuleCreates, executes, composes, and inspects constraints.
all: Constraint<'value> list -> Constraint<'value>Requires every constraint to hold, evaluating each in declaration order and accumulating failures. The empty list is the satisfied identity. F# visits list elements left to right, so annotate the binding when the first element is a type-directed value: let requiredName : Constraint<string> = Constraint.all [ Constraint.present; Constraint.lengthBetween 2 40 ]. let requiredName : Constraint<string> = Constraint.all [ Constraint.present; Constraint.lengthBetween 2 40 ]
present: Constraint<^value>Requires a value to be inhabited according to its shape. Whitespace-only text is blank, as are null text, a null or empty collection or map, None, ValueNone, and an empty Nullable. Blankness means .NET's whitespace set plus U+FEFF, which is what lets the rule be exported; see nonBlankPattern. The shape is selected from the return type, so a reusable binding needs its annotation: let requiredName : Constraint<string> = Constraint.present. Applied where the type is already known — inside an annotated rule, or to a schema — no annotation is needed. let requiredName : Constraint<string> = Constraint.present
lengthBetween: int -> int -> Constraint<^value>Requires a text or collection size inside the supplied inclusive bounds. let name : Constraint<string> = Constraint.lengthBetween 2 40
validateName: string -> Result<string,SignupError>value: string(|>): '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
guard: Constraint<'value> -> 'value -> Result<'value,Violation>Runs a constraint and returns the unchanged value after success. let requiredName : Constraint<string> = Constraint.present "Alice" |> Constraint.guard requiredName |> Result.mapError Violation.render
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
mapError: ('a -> 'b) -> Result<'c,'a> -> Result<'c,'b>Maps the error value of a result.
Render at the UI, log, or HTTP boundary:
let describe error =
match error with
| InvalidName violation -> $"Invalid name: {Violation.render violation}."
describe: SignupError -> stringerror: SignupErrorInvalidNameviolation: ViolationThis preserves the diagnostic for comparison, testing, localization, and other projections. Violation is not an
application error union; wrap it in the case that identifies the failed application operation or field.
Multiple failures preserve their meaning
Constraint.all reports every failed child. Violation.render separates those failures with ; :
""
|> Constraint.check name
|> Result.mapError Violation.render
// Error "value must be present; expected a size between 2 and 40, but was 0"
(|>): '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.ConstraintModuleCreates, executes, composes, and inspects constraints.
check: Constraint<'value> -> 'value -> Result<unit,Violation>Runs a constraint, returning why the value failed. let retryCount = Constraint.between 0 10 42 |> Constraint.check retryCount |> Result.mapError Violation.render
name: Constraint<string>Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
mapError: ('a -> 'b) -> Result<'c,'a> -> Result<'c,'b>Maps the error value of a result.
Reified.ViolationModuleInspects, traverses, and renders violations.
render: Violation -> stringRenders a violation as an English sentence fragment with no trailing punctuation, keeping conjunction and alternative groups distinct. Violation.render (Atomic (Expected (PresenceAtom Present, None))) // "value must be present"
Constraint.any reports the rejected alternatives only when every alternative fails. Rendering separates alternatives
with , or :
let ttl : Constraint<int> =
Constraint.any (Constraint.equalTo -1) [ Constraint.positive ]
0
|> Constraint.check ttl
|> Result.mapError Violation.render
// Error "expected -1, but was 0, or expected a value greater than 0, but was 0"
ttl: Constraint<int>Reified.Constraint`1A reusable description of valid values, coupled to the closures that execute it. One constraint value serves direct checking, refined-value admission, Schema, documentation, and export. There is no separate check type: check is the operation, Constraint is the noun. Both closures are retained deliberately. They are not duplicates of one rule: test over a conjunction may stop at the first failing child, while check must run every child to accumulate. Interpreted atoms and custom predicates therefore have a Boolean path that does no violation work, and combinators preserve that property when every child has it. A customWith constraint supplies only a violation-returning callback, so its test runs that callback and discards the error. The description is never interpreted during execution. Closures are composed once, at construction.
intAn abbreviation for the CLI type . Basic Types
Reified.ConstraintModuleCreates, executes, composes, and inspects constraints.
any: Constraint<'value> -> Constraint<'value> list -> Constraint<'value>Requires at least one alternative to hold, evaluating left to right and stopping at the first success. When none succeeds, every rejected branch is reported. Taking the first branch separately keeps an unsatisfiable empty disjunction unrepresentable, so this never throws. Alternatives among rules are what neither oneOf (alternatives among literals) nor a range (one contiguous region) can express — a valid set with a hole in it, such as a wire value that is either a sentinel or a duration. let ttl : Constraint<int> = Constraint.any (Constraint.equalTo -1) [ Constraint.atLeast 1 ]
equalTo: 'value -> Constraint<'value>Requires equality with the supplied value, under F# structural equality. let mustBeDraft : Constraint<Status> = Constraint.equalTo Status.Draft
positive: Constraint<^value>Requires a value strictly greater than zero. let quantity : Constraint<int> = Constraint.positive
(|>): '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
check: Constraint<'value> -> 'value -> Result<unit,Violation>Runs a constraint, returning why the value failed. let retryCount = Constraint.between 0 10 42 |> Constraint.check retryCount |> Result.mapError Violation.render
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
mapError: ('a -> 'b) -> Result<'c,'a> -> Result<'c,'b>Maps the error value of a result.
Reified.ViolationModuleInspects, traverses, and renders violations.
render: Violation -> stringRenders a violation as an English sentence fragment with no trailing punctuation, keeping conjunction and alternative groups distinct. Violation.render (Atomic (Expected (PresenceAtom Present, None))) // "value must be present"
The separators reflect the constraint tree: all means every reported condition was required, while any means the
value failed every permitted alternative.
Inspect a violation without parsing its message
Built-in failures carry the failing constraint atom and, when Reified can represent it, the actual value. Use the projections when code needs those facts:
match "ab" |> Constraint.check (Constraint.minLength 3: Constraint<string>) with
| Ok () -> ()
| Error violation ->
let expectation = Violation.tryExpectation violation
// Some (CardinalityAtom (Cardinality.Minimum 3))
let actual = Violation.tryActual violation
// Some (ConstraintValue.Integer 2L)The projections return None for a grouped violation because a group has more than one answer. Use
Violation.children for its immediate children, or Violation.flatten for every atomic failure in report order.
Opaque rules created with Constraint.custom carry author-supplied prose instead; read a single opaque leaf with
Violation.tryDescription.
Reified-produced groups are never empty or wrapped around one child. If only one child fails, that violation is returned directly.
Translate messages
Violation.render is the zero-dependency English default, not the only option. A Renderer carries the language
and the document context; the violation carries the facts:
let signup = renderer |> Renderer.context "signup"
violation |> Violation.message (signup |> Renderer.attribute "name")
// "must be present"
violation |> Violation.fullMessage (signup |> Renderer.attribute "name")
// "Name must be present"message renders a bare predicate, for a form row whose label already names the field. fullMessage composes the
attribute noun once around the whole message, for payloads and logs. Renderer.english needs no resources at all.
Violation.toMessageTree remains available for a localization system that must control word order across a whole
group. See Localization for rendering, with context and fallback,
advanced rendering for groups and plurals, and
the key catalogue. Adding a language covers generating a new
translation.