FS FormScript

Values and defaults

A field can hold three different kinds of thing, and they are not interchangeable:

The kind Written as Who decides it
an answer what the person types the person
a default default = 60 you, until somebody answers
a computed value value = … the document, always

default — the answer until there is one

forms
form loan version = "1.0" {
  field term {
    type = integer
    label = "Term in months"
    default = 60
  }
}

Until the applicant answers, term holds 60. When they answer 36, term holds 36 and the default is gone. A default is never computed and never recalculated; it is simply the answer until there is another one.

value — the document computes it

forms
form loan version = "1.0" {
  field term        { type = integer }
  field amount      { type = number }
  field monthly     { type = number  value = pmt(0.05 / 12, term, amount) }
  field total       { type = number  value = monthly * 12 }
}

monthly is not a question. It is an expression over two other fields, and it is recomputed whenever either of them changes.

You never assign to a value. There is no = that writes; the = in value = … is the same = that declares a property, and the right-hand side is an expression the runtime evaluates.

When a value recomputes

The runtime builds a dependency graph from the expressions and works out what to recompute. It is not a rule you write:

  • monthly reads term and amount, so it recomputes when either changes.
  • total reads monthly, so it recomputes when monthly changes — which is when term or amount changes.

You never write "recompute this when that changes". You write what a value means, and the graph follows.

There is one subtlety, and it is worth stating plainly: a value that reads a field which is not applicable does not get a stale answer, it gets unknown. See section 10.

A value and a default together

forms-invalid
// E-CONFLICT-01: a value and a default contradict each other.
form loan version = "1.0" {
  field notes {
    type = string
    default = "computed"
    value = "computed"
  }
}

This document is wrong, and the reference implementation says so: E-CONFLICT-01. A value and a default on the same field say contradictory things about the same field — one says "this is what the answer is", the other says "this is what the answer is until somebody answers". Pick one.

The output document

A form produces a document, and you say what it looks like in an output block:

forms
form loan version = "1.0" {
  field term    { type = integer }
  field amount  { type = number }
  field monthly { type = number  value = pmt(0.05 / 12, term, amount) }

  output {
    term: integer
    amount: number
    monthly: number
  }
}

The output block is a schema, not a projection. term: integer says "the output document has a key term whose type is integer" — the value comes from the field of the same name. You do not write term: term.

A computed field appears in the output like any other, under its own name.

Section 13 covers the output document properly, including what happens to a field that was never answered.