# Derivations

A schema is a complete, machine-readable description of your data. Because of
that, Effect can **derive** other artifacts directly from it — you write the
schema once and get an equivalence, a test-data generator, a human formatter, a
JSON codec, a JSON Patch differ, JSON Schema, and Standard Schema interop, all
for free.

Every derivation lives on the `Schema` namespace as a `to*` function. Each one
takes a schema and returns a plain value (an `Equivalence`, a `FastCheck.Arbitrary`,
a function, a `Codec`, a `Differ`, etc.) that you can use anywhere.

## The common case: one schema, many artifacts

Define a schema once, then derive whatever you need from it.

```ts
import { Schema } from "effect"
import * as FastCheck from "fast-check"

const Person = Schema.Struct({
  id: Schema.Number,
  name: Schema.String
})

// Structural equality
const eq = Schema.toEquivalence(Person)
eq({ id: 1, name: "Alice" }, { id: 1, name: "Alice" }) // => true
eq({ id: 1, name: "Alice" }, { id: 2, name: "Alice" }) // => false

// Random test data for property-based tests
const arb = Schema.toArbitrary(Person)
FastCheck.sample(arb, 1)[0] // => { id: -12, name: "..." } (random)

// Human-readable formatting
const format = Schema.toFormatter(Person)
format({ id: 1, name: "Alice" }) // => '{ "id": 1, "name": "Alice" }'

// A canonical JSON codec (Type <-> Json)
const json = Schema.toCodecJson(Person)

// A JSON Schema document
const jsonSchema = Schema.toJsonSchemaDocument(Person)
```

These derivations are **structural**: they walk the schema's AST and recurse
into structs, arrays, unions, and declarations. When the default behaviour is
not right for a given type, attach a custom one with the matching `overrideTo*`
helper (see [Overriding a derivation](#overriding-a-derivation)).
**Where each derivation is documented in depth:** JSON Schema generation has its own page — see [JSON Schema](https://effect.plants.sh/schema/json-schema/).
Property-based testing with derived arbitraries is covered in
[Writing tests](https://effect.plants.sh/testing/writing-tests/). The general-purpose `Equivalence`
trait lives in [Equivalence](https://effect.plants.sh/traits/equivalence/), and error/issue formatting
in [Error Formatting](https://effect.plants.sh/schema/error-formatting/).

## Equivalence

### `toEquivalence`

Derives an [`Equivalence.Equivalence<T>`](https://effect.plants.sh/traits/equivalence/) that compares two
values field-by-field according to the schema structure. Nested structs, arrays,
and unions are compared recursively.

```ts
import { Schema } from "effect"

const eq = Schema.toEquivalence(
  Schema.Struct({ id: Schema.Number, name: Schema.String })
)

eq({ id: 1, name: "Alice" }, { id: 1, name: "Alice" }) // => true
eq({ id: 1, name: "Alice" }, { id: 2, name: "Alice" }) // => false
```

### `overrideToEquivalence`

Attaches a custom equivalence to a schema so that `toEquivalence` uses it instead
of the derived structural comparison. Useful when two values should be treated as
equal even though they are not field-by-field identical.

```ts
import { Schema } from "effect"

// Compare names case-insensitively
const Name = Schema.String.pipe(
  Schema.overrideToEquivalence(() => (a, b) => a.toLowerCase() === b.toLowerCase())
)

const eq = Schema.toEquivalence(Name)
eq("Alice", "alice") // => true
eq("Alice", "Bob")   // => false
```

## Arbitraries (test data)

Derived arbitraries are [`fast-check`](https://fast-check.dev) `Arbitrary`
values, ready to feed into property-based tests. See
[Writing tests](https://effect.plants.sh/testing/writing-tests/) for how to run them with
`@effect/vitest`.

### `toArbitrary`

Derives a `FastCheck.Arbitrary<T>` that generates values satisfying the schema
(including any refinements such as length or range checks). Use this when you
just need the arbitrary directly.

```ts
import { Schema } from "effect"
import * as FastCheck from "fast-check"

const PersonArb = Schema.toArbitrary(
  Schema.Struct({ name: Schema.String, age: Schema.Number })
)

const sample = FastCheck.sample(PersonArb, 1)[0]
typeof sample.name // => "string"
typeof sample.age  // => "number"
```

### `toArbitraryLazy`

Derives a `LazyArbitrary<T>` — a function `(fc) => FastCheck.Arbitrary<T>` — that
defers instantiation until you pass it the `fast-check` module. Prefer this when
you need to control which `fast-check` instance is used, or for recursive schemas.
The result is memoized, so repeated calls are cheap.

```ts
import { Schema } from "effect"
import * as FastCheck from "fast-check"

const lazy = Schema.toArbitraryLazy(Schema.String)

const arb = lazy(FastCheck) // => FastCheck.Arbitrary<string>
FastCheck.sample(arb, 1)[0] // => "..." (random string)
```

## Formatter (human-readable strings)

### `toFormatter`

Derives a `Formatter<T>` — a function `(value: T) => string` — that renders a
value as a human-readable string, recursing into structs, arrays, and unions.
Strings are quoted, `bigint` literals get an `n` suffix, `Option` shows as
`some(...)` / `none()`, and so on. This is distinct from
[issue/error formatting](https://effect.plants.sh/schema/error-formatting/), which renders validation
failures.

```ts
import { Schema } from "effect"

const format = Schema.toFormatter(
  Schema.Struct({ id: Schema.Number, name: Schema.String })
)

format({ id: 1, name: "Alice" }) // => '{ "id": 1, "name": "Alice" }'

Schema.toFormatter(Schema.String)("a") // => '"a"'
Schema.toFormatter(Schema.BigInt)(1n)  // => "1n"
```

The optional `onBefore` hook lets you intercept specific AST nodes before the
default formatting runs:

```ts
import { Schema } from "effect"

const format = Schema.toFormatter(Schema.Number, {
  onBefore: (ast) => (ast._tag === "Number" ? (n) => `#${n}` : undefined)
})

format(42) // => "#42"
```

### `overrideToFormatter`

Attaches a custom formatter to a schema so that `toFormatter` uses it instead of
the structural default.

```ts
import { Schema } from "effect"

const Money = Schema.Number.pipe(
  Schema.overrideToFormatter(() => (cents) => `$${(cents / 100).toFixed(2)}`)
)

Schema.toFormatter(Money)(1299) // => "$12.99"
```

## Representation

### `toRepresentation`

Derives a `SchemaRepresentation.Document` — an intermediate, structural
representation of the schema (a `representation` plus its named `references`).
This is the IR that [`toJsonSchemaDocument`](https://effect.plants.sh/schema/json-schema/) is built on;
reach for it directly when you want to walk or transform the schema's shape
yourself.

```ts
import { Schema } from "effect"

const doc = Schema.toRepresentation(
  Schema.Struct({ name: Schema.String })
)

doc.representation // => the structural representation
doc.references     // => named references ($ref map)
```

## Alternate codecs

These derivations produce a new `Codec` whose **encoded** side is a canonical
serialization format. The decoded (`Type`) side is unchanged, so you can decode
and encode with the usual [parsing APIs](https://effect.plants.sh/schema/transformations/)
(`Schema.encodeSync`, `Schema.decodeSync`, etc.).

### `toCodecJson`

Derives a canonical `Codec<T, Json>` — encoding produces a JSON-compatible value
and decoding reconstructs the schema's `Type`. Types that are not natively JSON
(dates, `URL`, `bigint`, `Map`, `Set`, ...) are encoded through their built-in
JSON representation.

```ts
import { Schema } from "effect"

const codec = Schema.toCodecJson(
  Schema.Struct({ when: Schema.Date, name: Schema.String })
)

const json = Schema.encodeSync(codec)({
  when: new Date("2024-01-01T00:00:00.000Z"),
  name: "Alice"
})
// => { when: "2024-01-01T00:00:00.000Z", name: "Alice" }

Schema.decodeSync(codec)(json)
// => { when: Date(2024-01-01...), name: "Alice" }
```

### `toCodecIso`

Derives an isomorphic `Codec<Type, Iso>`. The encoded side is the schema's `Iso`
type — the intermediate representation used for lossless round-tripping (no
information is lost in either direction). For most schemas the `Iso` and `Type`
sides coincide; declarations may define a richer `Iso` form.

```ts
import { Schema } from "effect"

const codec = Schema.toCodecIso(Schema.Option(Schema.Number))

const iso = Schema.encodeSync(codec)
const back = Schema.decodeSync(codec)
// `iso`/`back` round-trip Option values through the Iso form losslessly
```

### `overrideToCodecIso`

Overrides the derived ISO codec with an explicit target codec and a
`decode` / `encode` getter pair. The resulting schema carries a custom `Iso`
type parameter.

```ts
import { Schema, SchemaGetter } from "effect"

// Expose a Date as an ISO string on the Iso side
const MyDate = Schema.Date.pipe(
  Schema.overrideToCodecIso(Schema.String, {
    decode: SchemaGetter.transform((s: string) => new Date(s)),
    encode: SchemaGetter.transform((d: Date) => d.toISOString())
  })
)

Schema.encodeSync(Schema.toCodecIso(MyDate))(new Date("2024-01-01T00:00:00.000Z"))
// => "2024-01-01T00:00:00.000Z"
```

### `toCodecStringTree`

Derives a `Codec<T, StringTree>` — every leaf value becomes a string while the
surrounding structure (objects, arrays) is preserved. This is the format behind
URL query-string and form-data encoding. Declarations are converted to
`undefined` unless they carry a `toCodecJson` / `toCodec` annotation.

```ts
import { Schema } from "effect"

const codec = Schema.toCodecStringTree(
  Schema.Struct({ page: Schema.Number, q: Schema.String })
)

Schema.encodeSync(codec)({ page: 2, q: "effect" })
// => { page: "2", q: "effect" }  // every leaf is a string

Schema.decodeSync(codec)({ page: "2", q: "effect" })
// => { page: 2, q: "effect" }
```

Pass `{ keepDeclarations: true }` to preserve non-string declaration values
(numbers, `Blob`, ...) when they are compatible with the schema — this is what
`Schema.fromFormData` uses to keep uploaded files intact:

```ts
import { Schema } from "effect"

const codec = Schema.toCodecStringTree(
  Schema.Struct({ a: Schema.Int }),
  { keepDeclarations: true }
)
```

### `toEncoderXml`

Derives an XML encoder from a codec. The returned function encodes a value
through `toCodecStringTree` and yields an `Effect` that succeeds with an XML
string (or fails with `SchemaError` if encoding fails). Options control the root
element name, array item name, pretty-printing, indentation, and key sorting.

```ts
import { Effect, Schema } from "effect"

const toXml = Schema.toEncoderXml(
  Schema.Struct({ name: Schema.String, age: Schema.Number }),
  { rootName: "person" }
)

Effect.runPromise(toXml({ name: "Alice", age: 30 })).then(console.log)
// => <person>
// =>   <age>30</age>
// =>   <name>Alice</name>
// => </person>
```

## Differ (JSON Patch)

### `toDifferJsonPatch`

Derives a `Differ<T, JsonPatch.JsonPatch>` from a codec. It
serializes values to JSON (via [`toCodecJson`](#tocodecjson)), computes RFC 6902
JSON Patch operations between an old and new value with `diff`, and can apply a
patch back to the typed value with `patch`. `patch` returns the original
reference when nothing changed.

```ts
import { Schema } from "effect"

const differ = Schema.toDifferJsonPatch(
  Schema.Struct({ a: Schema.String, b: Schema.Number })
)

const patch = differ.diff({ a: "x", b: 1 }, { a: "x", b: 2 })
// => [{ op: "replace", path: "/b", value: 2 }]

differ.patch({ a: "x", b: 1 }, patch)
// => { a: "x", b: 2 }

differ.empty                       // => []  (no-op patch)
differ.combine(patch, differ.empty) // => patch (concatenated ops)
```

## Standard Schema & JSON Schema

### `toStandardSchemaV1`

Produces a [Standard Schema v1](https://standardschema.dev) object so the schema
can be consumed by any Standard-Schema-compatible library (form libraries,
validators, etc.). The returned object exposes a `~standard.validate` method.
Optional `leafHook` / `checkHook` customize issue messages, and `parseOptions`
forwards parse settings (defaults to collecting all errors).

```ts
import { Schema } from "effect"

const Person = Schema.Struct({
  name: Schema.NonEmptyString,
  age: Schema.Number
})

const standard = Schema.toStandardSchemaV1(Person)

standard["~standard"].validate({ name: "Alice", age: 30 })
// => { value: { name: "Alice", age: 30 } }

standard["~standard"].validate({ name: "", age: 30 })
// => { issues: [{ path: ["name"], message: "..." }] }
```

### `toStandardJSONSchemaV1`

Produces an (experimental) Standard JSON Schema v1 object. It attaches a
`~standard.jsonSchema` accessor with `input` / `output` methods that emit JSON
Schema for the encoded and decoded sides, targeting either `draft-2020-12` or
`draft-07`.

```ts
import { Schema } from "effect"

const Person = Schema.Struct({ name: Schema.String })
const standard = Schema.toStandardJSONSchemaV1(Person)

standard["~standard"].jsonSchema.input({ target: "draft-2020-12" })
// => { type: "object", properties: { name: { type: "string" } }, ... }
```

### `toJsonSchemaDocument`

Returns a full JSON Schema **document** (draft 2020-12) with a `schema` and a
`definitions` map. This is the primary JSON Schema entry point — see the dedicated
[JSON Schema](https://effect.plants.sh/schema/json-schema/) page for options
(`additionalProperties`, `generateDescriptions`, `includeAnnotationKey`) and a
deeper walk-through.

```ts
import { Schema } from "effect"

const document = Schema.toJsonSchemaDocument(
  Schema.Struct({ name: Schema.String })
)

document.dialect      // => "draft-2020-12"
document.schema       // => { type: "object", properties: { name: ... }, ... }
document.definitions  // => {} (named $defs, if any)
```

## Optics

These derive optics from a schema's `Iso` boundary, letting you focus into the
serialized form.

### `toIso`

Derives an `Iso<Type, Iso>` optic that isomorphically converts between the
schema's `Type` and its `Iso` (intermediate / serialized) form, built on top of
[`toCodecIso`](#tocodeciso).

```ts
import { Schema } from "effect"

const iso = Schema.toIso(Schema.Option(Schema.Number))
// => Iso focusing the Iso representation of Option<number>
```

### `toIsoSource`

Returns the identity `Iso<Type, Type>` over the schema's source (decoded) side —
a convenient starting point when composing optics that stay on the `Type` side.

```ts
import { Schema } from "effect"

const iso = Schema.toIsoSource(Schema.String) // => Iso<string, string>
```

### `toIsoFocus`

Returns the identity `Iso<Iso, Iso>` over the schema's focus (encoded `Iso`)
side — the counterpart to `toIsoSource` for composing on the `Iso` side.

```ts
import { Schema } from "effect"

const iso = Schema.toIsoFocus(Schema.String) // => Iso over the Iso side
```

## Overriding a derivation

Every structural derivation can be customized per-schema by attaching an
annotation through the matching `overrideTo*` helper. The override is picked up
the next time you call the corresponding `to*` function on that schema (or any
schema that contains it).

| Derivation | Override helper |
| --- | --- |
| `toEquivalence` | `overrideToEquivalence` |
| `toFormatter` | `overrideToFormatter` |
| `toCodecIso` | `overrideToCodecIso` |

```ts
import { Schema } from "effect"

// One schema, two overrides — both flow through the derivations below
const Temperature = Schema.Number.pipe(
  Schema.overrideToFormatter(() => (c) => `${c}°C`),
  Schema.overrideToEquivalence(() => (a, b) => Math.round(a) === Math.round(b))
)

Schema.toFormatter(Temperature)(21.4)         // => "21.4°C"
Schema.toEquivalence(Temperature)(21.4, 21.0) // => true
```
**Note:** Overrides are stored as schema annotations, so they compose: an override on a
field automatically applies when you derive an artifact for a struct that
contains that field.