# Constants

A `constant` names a fixed value at the top of a file. It is the same in
every deployment, worked out while compiling, and never changes.

```jadpo
type Stock = Int {
    min: 0
}

constant bulk_from: Stock = 5000
constant small_batch: Stock = 250

function batch_for(needed: Stock) -> Stock {
    if (needed >= bulk_from) {
        return 0
    }
    return small_batch
}
```

- **Form.** `constant name: Type = value`. The name is lower_snake_case,
  like any value. There is no mutable form: `constant mut` is
  `syntax.expected_name`, whose edit removes `mut`.
- **Only at the top of a file.** Inside a body a fixed value is always a
  `var`, and at the top of a file always a `constant`, so each place has
  one way to write it. `constant` in a body is
  `syntax.expected_statement`, whose edit writes `var`. A `var` at the top
  of a file is `syntax.expected_declaration`, whose `next:` says to write
  `constant`. `const` at the top of a file is `syntax.variable_keyword`,
  whose edit writes `constant`; in a body it is also
  [`syntax.variable_keyword`](../errors/syntax.variable_keyword.md), whose
  edit writes `var`.
- **A named type.** A constant's type follows the same rule as a
  signature's ([Signatures use named types](signatures-use-named-types.md)):
  `constant limit: Int = 5` is `type.primitive_signature`, as `limit: Int`
  is in a parameter. A list of a named type, an object, an enum, `Email`,
  `Url` and the other types a signature may use are all fine.
- **A value the compiler can work out.** Literals, enum values, other
  constants, values constructed from those (`Stock(5000)`, an object, a
  list), and the operators of
  [Expressions and operators](expressions-and-operators.md) on them. No call
  of any kind (functions, library functions, the standard library), no
  `config`, no `clock`, and no constant that depends on itself
  ([`constant.value_not_fixed`](../errors/constant.value_not_fixed.md),
  [`constant.cycle`](../errors/constant.cycle.md)). Arithmetic gives the
  built-in type underneath, as it does in a body, so `Stock(bulk_from * 2)`
  constructs a `Stock` from it again.
- **Checked while compiling.** The value is checked against every rule of
  its type and of the types it is built on, as a literal is
  ([`type.invalid_literal`](../errors/type.invalid_literal.md), which
  names the value worked out). So constructing a value in a constant never
  needs `attempt`. When the app runs, the constant's value is worked out
  where it is read, and that can't fail.
- **Kept together.** `jadpo fmt` keeps constants written one after another
  on consecutive lines, as you grouped them, and puts a blank line between
  every other pair of declarations.
- **Unused is an error**, as for any other declaration. Beside a file's
  primary declaration (the one the file is named after) it is
  `layout.unused_declaration`. In a library, a constant nothing reads is
  unused, as a function nothing calls is.

Constants built from other constants, a list of them, and both read in
functions:

```jadpo
type Stock = Int {
    min: 0
    max: 100000
}

type LargeOrder = Bool {}

type BatchCharged = Bool {}

constant small_batch: Stock = 250
constant large_batch: Stock = Stock(small_batch * 2)
constant batch_sizes: List<Stock> = [small_batch, large_batch]

function batch_chosen(large: LargeOrder) -> Stock {
    if (large) {
        return large_batch
    }
    return small_batch
}

function charged(batch: Stock) -> BatchCharged {
    return BatchCharged(batch in batch_sizes)
}
```

Each of these mistakes is one error, with its edit:

```jadpo
type Stock = Int {
    min: 0
}

function batch() -> Stock {
    constant small_batch: Stock = 250
    return small_batch
}
```

```text
error: `constant` doesn't start a statement
 --> main.jadpo:6:5
  |
6 |     constant small_batch: Stock = 250
  |     ^^^^^^^^ write `var`
  reason: A constant lives at the top of a file or in a library; inside a body a value that never changes is a `var`.
  next: Use `var` instead of `constant` (agent)
  rule: syntax.expected_statement
  help: https://jadpo.dev/docs/errors/syntax.expected_statement

check failed: 1 error, 1 file
```

```jadpo
type Stock = Int {
    min: 0
}

constant mut small_batch: Stock = 250
```

```text
error: A constant can't be `mut`
 --> main.jadpo:5:10
  |
5 | constant mut small_batch: Stock = 250
  |          ^^^ remove `mut`
  reason: A constant never changes, so it has no mutable form; a value that changes is a `var mut` inside a body.
  next: Remove `mut` (agent)
  rule: syntax.expected_name
  help: https://jadpo.dev/docs/errors/syntax.expected_name

check failed: 1 error, 1 file
```

```jadpo
type Stock = Int {
    min: 0
}

const small_batch: Stock = 250
```

```text
error: Jadpo writes `constant`, not `const`
 --> main.jadpo:5:1
  |
5 | const small_batch: Stock = 250
  | ^^^^^ write `constant`
  reason: `const` declares a value that never changes in JavaScript, TypeScript, Rust, Go and C#; at the top of a Jadpo file a fixed value is `constant name: Type = value`.
  next: Use `constant` instead of `const` (agent)
  rule: syntax.variable_keyword
  help: https://jadpo.dev/docs/errors/syntax.variable_keyword

check failed: 1 error, 1 file
```

```jadpo
constant max_login_attempts: Int = 5
```

```text
error: This constant uses `Int` directly
 --> main.jadpo:1:30
  |
1 | constant max_login_attempts: Int = 5
  |                              ^^^ a built-in type
  reason: A constant's type is named as a signature's is, so its value says what it means and carries its rules; `Int` says neither.
  next: Declare a named type built on `Int` and use it here (agent)
  rule: type.primitive_signature
  help: https://jadpo.dev/docs/errors/type.primitive_signature

check failed: 1 error, 1 file
```

## Where a constant lives

A constant goes where a helper function would
([Sharing and promotion](sharing-and-promotion.md)).
A constant one file uses lives at the top of that file, beside the
declaration that uses it. It is never a file's primary declaration, so
there is no `constants/` folder, and its name never clashes with a file's
name: `constant vat_rate: VatRate` is fine, because
`naming.folded_collision` leaves constants out.

When a second file uses it, `layout.promotion_required` asks you to move
it into a library. Code then reads it through the library, like a library
function but without brackets:

```jadpo
type Stock = Int {
    min: 0
}

type HoldbackRate = Decimal {
    scale: 4
    min: 0.0000
    max: 1.0000
}

library stocking {
    /// The standard share held back.
    constant holdback_rate: HoldbackRate = 0.2000

    function holdback_of(per_pack: Stock) -> Stock {
        return Stock(per_pack / 5)
    }
}

function standard_holdback() -> HoldbackRate {
    var uk_rate: HoldbackRate = stocking.holdback_rate
    return uk_rate
}

function holdback(per_pack: Stock) -> Stock {
    return stocking.holdback_of(per_pack)
}
```

A library's own functions and constants read its constants by name alone,
as they call its functions.

A library a second domain uses moves to `shared/libraries/`, or, when it
depends on its domain's types, stays and becomes `public`, as it would for
functions. A private constant's name must be unique only within its
domain, as private functions' and types' names are
([Files and names](files-and-names.md)). Its
identity key, the name Jadpo's records know it by
([Stable identity](stable-identity.md)), carries
its domain: `constant billing.free_delivery_from`.

## Where a constant can be read

Anywhere a value can, functions included, because it never changes. It is
also what "constants" means in the places that allow only fixed values:

- **A query** accepts a constant wherever it accepts a literal; anything
  else there is `query.value_unsupported`
  ([Named queries](named-queries.md)).
- **An update by `id`** may use parameters, constants, `clock.now` and the
  stored fields
  ([The operations every entity gets](the-operations-every-entity-gets.md)).
- **A computed backfill** may use the record's stored fields, constants
  and pure functions ([Computed backfills](computed-backfills.md)).

A setting's `default:` and `test:` take a literal, never a constant (see
[A constant or a setting](constants.md#a-constant-or-a-setting)).

## Two constants holding one value

Agents working at the same time tend to invent the same constant twice,
each writing its own `vat_rate`. Two constants of the same type holding
the same value are an error,
[`constant.similar_exists`](../errors/constant.similar_exists.md), as two
alike functions are
[`library.similar_exists`](../errors/library.similar_exists.md). The error
names the other constant and its file, so the agent reads that one and
removes its own. The first in file and source order is the one named, and
the others are reported.

A VAT rate and a fee can share a value by coincidence, so the error is a
judgement call you can decline: a `decline constant.similar_exists:
"<why>"` line above the constant answers it
([Declining a judgement call](declining-a-judgement-call.md)).

The library catalogue, the list of shared code that `jadpo check` writes
into `build/catalogue/` ([Tooling](tooling.md)), lists each constant
with its type, value, `///` description and the code that reads it.

## A constant or a setting

If a deployment could ever need a different value, make it a setting in
`config.jadpo` ([Configuration and time](configuration-and-time.md));
otherwise make it a constant. A page size an operator might tune is a
setting. The number of pence in a pound is a constant. The Jadpo agent
skill gives agents the same rule
([Tools for agents](tools-for-agents.md)).

A value is one or the other, never both, so it has one source and one way
to read it. A constant is fixed and read as
`security.max_login_attempts`. A setting is chosen per deployment and read
as `config.max_login_attempts`.

So a setting's `default:` never names a constant
(`configuration.default_literal_required`). If it could, code reading the
constant directly would quietly ignore the deployment's value. Say
`security.standard_attempts` (5) were `max_login_attempts`'s default, and
someone set `MAX_LOGIN_ATTEMPTS=10`: code reading
`security.standard_attempts` would still allow 5. You don't need the
constant anyway, because the setting's `default:` already holds the
default.

From fixed to live, there are three kinds of value:

- a `constant` is fixed in the code;
- a `config` setting is chosen per deployment;
- a flag in `flags { }` changes while the app runs
  ([Feature flags](feature-flags.md)).

**Making a constant a setting** is a job for a tool: `jadpo config move
<constant>`, or the editor's "Make this a setting"
([Tooling](tooling.md)). It adds a field of the same name and type to
`config.jadpo` whose `default:` is the constant's value, deletes the
constant, and rewrites every read (`security.max_login_attempts` becomes
`config.max_login_attempts`). The reviewer then sees each place whose
behaviour can now differ per deployment.

It refuses, editing nothing, a constant:

- whose type a setting can't hold;
- whose name a setting already has;
- read where a setting can't be: in a query, an update by `id`, a backfill
  or another constant's value.

The details are in
[Making a constant a setting](settings-config-jadpo.md#making-a-constant-a-setting).

## Values that change on a known date

Some values change on a date you know in advance, such as the UK's VAT
rate. Jadpo has no setting for this (there is no `effective:`). Write a
constant holding a dated list, and read it through a library function:

```jadpo
type VatRate = Decimal {
    scale: 4
    min: 0.0000
    max: 1.0000
}

type VatDate = CalendarDate {}

type VatBand = Object {
    valid_from: VatDate
    band_rate: VatRate
}

type VatBands = List<VatBand> {
    min_length: 1
}

library vat {
    constant uk_bands: VatBands = [
        VatBand {
            valid_from: "2010-01-01"
            band_rate: 0.1750
        },
        VatBand {
            valid_from: "2011-01-04"
            band_rate: 0.2000
        }
    ]

    /// The UK standard rate on a date.
    function rate_on(day: VatDate) -> VatRate {
        var mut found: VatRate = 0.0000
        for band in uk_bands {
            if (band.valid_from <= day) {
                found = band.band_rate
            }
        }
        return found
    }
}

function vat_rate_on(day: VatDate) -> VatRate {
    var vat_due_rate: VatRate = vat.rate_on(day)
    return vat_due_rate
}
```

- A change of rate is an edit to the list, reviewed as a diff. Test it
  with the test clock on each side of the date ([Testing](testing.md)).
- Make it an entity only when someone who isn't a developer must change it
  while the app runs.
- A record stores the rate it used (an invoice's `vat_rate` field), so a
  later change never rewrites what was charged.
