`in`, reading facts
in reads the facts handed to the execution. Every top-level key of the input is also bound under
its own name, so order["total"] works directly. in is what you use when you want a read that
cannot raise, a default, or a dotted path into nested objects.
That distinction is why the namespace exists. A bare fact name the caller did not send aborts the
whole execution, even in the when of a rule that has nothing to do with it
(non existent key optional_field), and dot access on a map fact (order.total) aborts too. Every
function here returns a default instead.
The surface is twelve functions:
| Function | Returns |
|---|---|
in.Has(path) | Whether anything is at path |
in.Eq(path, value) · in.EqInt · in.EqNum | Equality, false when absent |
in.Str · in.Int · in.Double · in.Bool | The value, or your default |
in.Date(path) | A date, or the zero date |
in.Value(path) | The typed value, or Null |
in.Map(path) · in.Rows(path) | A nested object, or table rows |
Presence and equality
Section titled “Presence and equality”in.Has(path) → bool
Section titled “in.Has(path) → bool”Reports whether anything is present at path.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
rule RequireApplication "nothing to do without an application" salience 100 { when !in.Has("application") then findings.Critical("RequireApplication", "input", "No application supplied"); out.Set("status", "incomplete"); Complete();}{ "applicant": { "name": "Ana Silva" } }{ "status": "incomplete" }in.Eq(path, value) → bool
Section titled “in.Eq(path, value) → bool”Reports whether the fact at path equals value. False when absent, which is the point: it
cannot raise, so it needs no guard.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
value | string | The string to compare against |
rule OpenOrdersOnly "only process open orders" { when in.Eq("order.status", "open") then out.Set("processed", true);}{ "order": { "id": "SO-1042", "status": "open" } }{ "processed": true }Written as order["status"] == "open", the same test raises when order was not sent. in.Eq is
the form that survives incomplete input, which on extracted documents is most input.
in.EqInt(path, value) → bool
Section titled “in.EqInt(path, value) → bool”Reports whether the integer fact at path equals value. False when absent.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
value | int64 | The whole number to compare against |
Prefer this over in.EqNum for anything conceptually a whole number: a page number, a count,
a term in months, so the literal at the call site is a bare 1 rather than 1.0.
rule FirstPageOnly "classification runs on page one" { when in.EqInt("page", 1) then out.Set("classified", true);}{ "page": 1 }{ "classified": true }in.EqNum(path, value) → bool
Section titled “in.EqNum(path, value) → bool”Reports whether the numeric fact at path equals value. False when absent.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
value | float64 | The number to compare against |
rule ExactRate "the rate is exactly the promotional one" { when in.EqNum("loan.rate", 6.125) then out.Set("promotional", true);}{ "loan": { "rate": 6.125 } }{ "promotional": true }Typed reads
Section titled “Typed reads”Each takes a default and returns it when the path is absent, at any level. None of them raise.
The default covers absence only. A value that is present but does not convert reads as the type’s
zero: "n/a" through in.Double is 0.0, not your default. For text that may not be a number,
read it with in.Str and convert with to.NumOr.
in.Str(path, def) → string
Section titled “in.Str(path, def) → string”| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
def | string | Returned when absent |
A number or date at the path comes back as text.
rule DefaultChannel "unstated channel is 'branch'" { when in.Has("application") then out.Set("channel", in.Str("application.channel", "branch"));}{ "application": { "id": "A-2291" } }{ "channel": "branch" }in.Int(path, def) → int64
Section titled “in.Int(path, def) → int64”| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
def | int64 | Returned when absent |
in.Double(path, def) → float64
Section titled “in.Double(path, def) → float64”| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
def | float64 | Returned when absent |
The same decimal-point rule applies to the default: write 0.0, not 0. in.Double("x", 0)
loads cleanly and then aborts the execution when it runs.
rule SeniorApproval "large invoices need a senior approver" { when in.Double("invoice.total", 0.0) > 1000.0 then out.Set("approver", "senior");}{ "invoice": { "number": "INV-88", "total": 1500.0 } }{ "approver": "senior" }in.Bool(path, def) → bool
Section titled “in.Bool(path, def) → bool”| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
def | bool | Returned when absent |
Text reads leniently: true, yes, y, 1, checked and x are true, in any case, and any
other text is false. That is how the Yes/No encoding document extraction produces lands
correctly.
in.Date(path) → Date
Section titled “in.Date(path) → Date”Returns the date at path, or the zero date.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
A text fact parses as YYYY-MM-DD, MM/DD/YYYY, M/D/YYYY, MM-DD-YYYY or YYYY/MM/DD. There is
no sentinel to compare against: an absent or unparseable date is IsZero(), not the literal
0001-01-01 you might be tempted to test for.
A Date offers three methods:
| Method | Returns |
|---|---|
IsZero() | Whether the date is absent or unparseable. The test to use |
String() | The canonical YYYY-MM-DD rendering |
Time() | The underlying time, for the time namespace’s arithmetic and comparisons |
Time() is the bridge: time.DaysBetween and time.IsBefore take times, so a date read from a
fact reaches them through it.
rule ApplicationAge "applications older than 90 days are stale" { when !in.Date("application.date").IsZero() && time.DaysBetween(in.Date("application.date").Time(), time.Now()) > 90 then findings.Warn("ApplicationAge", "freshness", "Application is more than 90 days old");}out.Set writes a Date in YYYY-MM-DD form:
rule ApplicationDated "the application carries a readable date" { when !in.Date("application.date").IsZero() then out.Set("applicationDate", in.Date("application.date"));}{ "application": { "date": "05/01/2026" } }{ "applicationDate": "2026-05-01" }in.Value(path) → Value
Section titled “in.Value(path) → Value”Returns the typed value at path, or Null: the general form. Use it when you want to ask the
value about itself rather than convert it immediately.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
What a fact Value offers
Section titled “What a fact Value offers”This is the engine’s own Value, distinct from the
document read Value that bbox.Doc().Right(...) returns. Both
in.Value and out.Value produce it.
| Method | Returns |
|---|---|
IsNull() | Whether the value is absent |
Kind() | The value’s type, for diagnostics |
Str() | Text. Numbers and dates in canonical form; a null is "" |
Int() | An integer, parsing or truncating as needed |
Double() | A float |
Bool() | A boolean, read leniently so the Yes/No encoding used across document extraction lands correctly |
Date() | A Date, parsing a string if needed. A non-date returns the zero date |
Map() | The nested object, or nil |
Rows() | The table rows, or nil |
Any() | The underlying value |
Equal(other) | Equality, comparing across numeric kinds: an integer 1 equals a float 1 |
Attributes() | The producer’s metadata, or nil |
WithAttr(name, val) | A copy carrying one more attribute |
WithAttrs(attrs) | A copy with the given attributes merged over any existing ones. The map is copied, so yours stays yours |
Equal comparing across numeric kinds is the useful one: it removes the integer-versus-float trap
that catches direct comparisons elsewhere in the language.
The two With* methods return copies rather than mutating, so the result has to be used, passed
to out.SetValue, or it is discarded. Attributes travel beside the value: they are readable with
out.AttrStr and do not appear in
the output map itself.
rule TagProvenance "mark this value as derived rather than read" { when in.Has("computed.ratio") then out.SetValue("ratio", in.Value("computed.ratio").WithAttr("source", "computed"));}{ "computed": { "ratio": 0.42 } }{ "ratio": 0.42 }in.Map(path) → map
Section titled “in.Map(path) → map”Returns the map at path, or nil.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
Pass it straight to a call: a local variable cannot hold a map (the load-time check rejects
m = in.Map(...)).
rule CopyBorrower "carry the borrower block through unchanged" { when in.Has("application.borrower") then out.Set("borrower", in.Map("application.borrower"));}{ "application": { "borrower": { "name": "Ana Silva", "years": 4 } } }{ "borrower": { "name": "Ana Silva", "years": 4 } }in.Rows(path) → list of map
Section titled “in.Rows(path) → list of map”Returns the table rows at path, or nil.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The fact name, or a dotted path |
rule HasLineItems "the input carried line items" { when array.Len(to.List(in.Rows("invoice.lines"))) > 0 then out.Set("lineCount", array.Len(to.List(in.Rows("invoice.lines"))));}{ "invoice": { "lines": [ { "sku": "A-1", "amount": 10.0 }, { "sku": "B-7", "amount": 5.5 } ] } }{ "lineCount": 2 }Dotted paths
Section titled “Dotted paths”Every path argument accepts a dotted path, application.borrower.name, reaching into nested
objects without a chain of accessors. A missing segment anywhere along the path returns your
default.
| Path | Reads |
|---|---|
"application.borrower.name" | name inside borrower inside application |
"a\\.b" | The top-level key a.b. \. (written \\. inside a GRL string) is a literal dot |
"Work Phone Ext." | The key as written. A dot that would leave an empty segment is kept literally |
A path cannot index into a list: read the list with in.Rows, then use the
array functions.
Deprecated
Section titled “Deprecated”These still load and run, with a warning from the load-time check naming the replacement. Write new rules against the forms above.
| Deprecated | Write instead | Notes |
|---|---|---|
in.Get(key) | in.Value(path) | Returns a typed value rather than a bare any |
in.GetStr(key) | in.Str(path, def) | Takes a default |
in.GetInt(key) | in.Int(path, def) | Takes a default |
in.GetFloat(key) | in.Double(path, def) | Takes a default |
in.GetBool(key) | in.Bool(path, def) | Takes a default |
in.GetMap(key) | in.Map(path) | Same shape, current name |
in.MemSet / MemGet / MemGetStr / MemGetRows / MemDelete / MemClear | out.Set | A value one rule computes for another belongs in out, which later rules read directly |
in.GetMemAsMap() | out.Set | Builds an envelope field by field; out.Set takes the value directly |
in.NewMapWithValues(k, v, …) | map.Of(k, v, …) | The same map, from the collections namespace |
The deprecated Get* reads follow dotted paths like the current ones and return the type’s zero
value when absent. Scratch memory is a value later rules can read but the caller never receives.
Values a later rule needs go in out: a later rule’s when sees an earlier rule’s
out write with no further call.
See also
Section titled “See also”- Reading facts: the same surface as a guide
out: writing results: the other half of the pairto: conversion: converting what you readmapandarray: working with whatin.Mapandin.Rowsreturn