Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
10de4c2
doc/semantics: add error handling design + Lean 4 skeleton
antiguru May 17, 2026
bf26c0e
doc/semantics: add NOT, IsErr, idempotence, conditional commutativity
antiguru May 17, 2026
21673bd
doc/semantics: add might_error analyzer + soundness + reorder safety
antiguru May 17, 2026
0a76308
doc/semantics: add strictness predicates and propagation theorems
antiguru May 17, 2026
7867c8b
doc/semantics: add evalCoalesce and the error-rescue laws
antiguru May 17, 2026
f0b00c4
doc/semantics: add identity laws and variadic And/Or with absorption
antiguru May 17, 2026
3f534a7
doc/semantics: wire variadic ops into Expr as list-carrying ctors
antiguru May 17, 2026
280f400
doc/semantics: tighten might_error for andN/orN with structural sound…
antiguru May 17, 2026
39eda42
doc/semantics: tighten might_error for coalesce via argsAllMightError
antiguru May 18, 2026
4cf032a
doc/semantics: lift variadic absorption through eval
antiguru May 18, 2026
cb27ccb
doc/semantics: add bag semantics — filterRel, project, basic laws
antiguru May 18, 2026
a1adb59
doc/semantics: add BagStream — explicit data/error stream pair
antiguru May 18, 2026
b684e16
doc/semantics: predicate pushdown across projection via Expr.subst
antiguru May 18, 2026
21d65a3
doc/semantics: add DiffWithError — semiring extension for global errors
antiguru May 18, 2026
00e63e5
doc/semantics: add UnifiedStream — single-carrier data + error model
antiguru May 18, 2026
86939a5
doc/semantics: clean up unused simp args in UnifiedStream helpers
antiguru May 18, 2026
800a669
doc/semantics: add aggregates — strict reduction over List Datum
antiguru May 18, 2026
aeba830
doc/semantics: add aggTry — non-strict (try_*) aggregate variants
antiguru May 18, 2026
773a180
doc/semantics: add Consolidate — diff-summation absorption laws
antiguru May 18, 2026
9fb971b
doc/semantics: tie DiffWithError to (Row, Time, Diff) triple stream
antiguru May 18, 2026
43fd98a
doc/semantics: add Join — cross product and predicate join on Unified…
antiguru May 18, 2026
648b1d4
doc/semantics: add GROUP BY — groupBy + aggregateBy
antiguru May 18, 2026
e2254c9
doc/semantics: spec-faithful err-distinct GROUP BY
antiguru May 18, 2026
d320471
doc/semantics: add BagStream.project with explicit err routing
antiguru May 18, 2026
1ce936f
doc/semantics: join cardinality theorems
antiguru May 18, 2026
0c26743
doc/semantics: GROUP BY cardinality theorems
antiguru May 18, 2026
6d287ce
doc/semantics: wire DiffWithError into UnifiedStream
antiguru May 18, 2026
8b2371a
doc/semantics: error diff absorbs through filter and cross
antiguru May 18, 2026
ceaf652
doc/semantics: row-keyed consolidation on UnifiedStream
antiguru May 18, 2026
56fba90
doc/semantics: cardinality and no-error for consolidate
antiguru May 18, 2026
8dd1e56
doc/semantics: groupByErrDistinct = groupBy on no-err inputs
antiguru May 18, 2026
406f717
doc/semantics: per-(row, time) consolidation
antiguru May 18, 2026
422bfb5
doc/semantics: cross is associative
antiguru May 18, 2026
69df13a
doc/semantics: tighten might_error with short-circuit detection
antiguru May 18, 2026
60a254d
doc/semantics: tighten might_error for IfThen
antiguru May 18, 2026
5d122f3
doc/semantics: extend short-circuit detection to andN / orN
antiguru May 18, 2026
3cf40df
doc/semantics: BagStream.filter commutativity
antiguru May 18, 2026
63b36b4
doc/semantics: BagStream project / filter data-side pushdown
antiguru May 18, 2026
fe9d647
doc/semantics: no-error preservation for cross and filter
antiguru May 18, 2026
f473bff
doc/semantics: DiffWithError mul_comm + Int specializations
antiguru May 18, 2026
91d0a07
doc/semantics: refresh roadmap
antiguru May 18, 2026
644e0fc
doc/semantics: rebase Triple onto TimedUnifiedRecord
antiguru May 18, 2026
c05eb1f
doc/semantics: add Datum.int and EvalError.divisionByZero
antiguru May 18, 2026
356d7cb
doc/semantics: numeric arithmetic and divide-by-zero
antiguru May 18, 2026
ff3d103
doc/semantics: tighten might_error for divide with literal divisor
antiguru May 18, 2026
386acf8
doc/semantics: ErrPropagating / NullPropagating instances for arithmetic
antiguru May 18, 2026
e961666
doc/semantics: UnifiedStream.project
antiguru May 18, 2026
e7491ae
doc/semantics: UNION ALL and UNION on UnifiedStream
antiguru May 18, 2026
d026fd6
doc/semantics: EXCEPT ALL (signed-diff flavor) on UnifiedStream
antiguru May 18, 2026
a35bc8e
doc/semantics: sign normalization and bag-semantics EXCEPT ALL
antiguru May 18, 2026
2ec8ee1
doc/semantics: DISTINCT on UnifiedStream
antiguru May 18, 2026
31c7cef
doc/semantics: comparison operators eq and lt
antiguru May 18, 2026
8a6aa08
doc/semantics: column-reference analyzer (join pushdown foundation)
antiguru May 18, 2026
5dc4982
doc/semantics: right-side column shift for join pushdown
antiguru May 18, 2026
b64c451
doc/semantics: bound monotonicity for column references
antiguru May 18, 2026
920df20
doc/semantics: colShift monoid laws
antiguru May 18, 2026
775194b
doc/semantics: operator distributivity over unionAll
antiguru May 18, 2026
6986044
doc/semantics: negation distributes through multiplication
antiguru May 18, 2026
212d76f
doc/semantics: negate commutes with consolidate
antiguru May 18, 2026
1056269
doc/semantics: clampPositive and clampToOne idempotence
antiguru May 18, 2026
221a279
doc/semantics: no_error preservation for set-op pipeline
antiguru May 18, 2026
7385b02
doc/semantics: identity-element theorems for set operations
antiguru May 18, 2026
5517825
doc/semantics: strict cardinality bound for consolidate duplicates
antiguru May 18, 2026
317e8aa
doc/semantics: column-unused analyzer (pruning foundation)
antiguru May 18, 2026
dfe032b
doc/semantics: bridge bounded and unused analyzers
antiguru May 18, 2026
0f92ec2
doc/semantics: bagExceptAll nil-left reduction
antiguru May 18, 2026
ca02c34
doc/semantics: named reduction lemmas for filter and cross
antiguru May 18, 2026
df6bd79
doc/semantics: more reduction lemmas for project and negate
antiguru May 18, 2026
25703a4
doc/semantics: named reduction lemmas for consolidateInto
antiguru May 18, 2026
7c45ebd
doc/semantics: named reduction lemmas for insertInto / insertIntoDist…
antiguru May 18, 2026
3c29a50
doc/semantics: errorRows reduction lemmas
antiguru May 18, 2026
7d50459
doc/semantics: INTERSECT ALL via min combinator
antiguru May 18, 2026
10bef2a
doc/semantics: intersectAll left-side error preservation
antiguru May 18, 2026
fe35552
doc/semantics: consolidate carrier uniqueness + right-side intersect
antiguru May 18, 2026
4d89ec8
doc/semantics: clampPositive is no-op on clampToOne output
antiguru May 18, 2026
4b75da8
doc/semantics: intersectAll no-error preservation
antiguru May 18, 2026
187e341
doc/semantics: bag-semantics INTERSECT ALL
antiguru May 18, 2026
25952da
doc/semantics: NoDupCarriers closure for negate and clampPositive
antiguru May 18, 2026
1657cc2
doc/semantics: clampToOne NoDup preservation + filter clarification
antiguru May 18, 2026
a045073
doc/semantics: NoDup closure for all derived set operators
antiguru May 18, 2026
56e50f9
doc/semantics: TimedUnifiedStream.advanceFrontier
antiguru May 18, 2026
408e67f
doc/semantics: error-scope extractors and invariance
antiguru May 18, 2026
2744f10
doc/semantics: filter error-scope theorems
antiguru May 18, 2026
1774fbe
doc/semantics: consolidate error-scope theorems
antiguru May 18, 2026
bce62ea
doc/semantics: cross error-scope propagation
antiguru May 18, 2026
12a057a
doc/semantics: escalateRowErrs operator
antiguru May 18, 2026
2f8377a
doc/semantics: project error-scope theorems
antiguru May 18, 2026
d93635e
doc/semantics: consolidate errorDiff iff (reverse direction)
antiguru May 18, 2026
028114c
doc/semantics: join error-scope theorems
antiguru May 18, 2026
93b4e01
doc/semantics: union and exceptAll error-scope theorems
antiguru May 18, 2026
7d850e2
doc/semantics: README updates for error-scope theorems
antiguru May 18, 2026
d891187
doc/semantics: filter pushdown for cross products
antiguru May 18, 2026
52a233a
doc/semantics: negate_filter commutes
antiguru May 18, 2026
d544af5
doc/semantics: clampPositive/clampToOne error-scope; distinct/bagExce…
antiguru May 18, 2026
cb07c35
doc/semantics: intersectAll error-scope reverse direction
antiguru May 18, 2026
65d722d
doc/semantics: timed error-scope extractors
antiguru May 18, 2026
8bd4bbc
doc/semantics: README updates for clamp/intersect/timed/JoinPushdown
antiguru May 18, 2026
dc71e10
doc/semantics: sumAll error inversion + Triple reverse direction
antiguru May 18, 2026
7361bdd
doc/semantics: Triple round-trip iff + errorDiffCarriers bridge
antiguru May 18, 2026
89694c3
doc/semantics: negate_project commute
antiguru May 18, 2026
cb42aa3
doc/semantics: cross_negate_right symmetric law
antiguru May 18, 2026
0e4c950
doc/semantics: transforms catalog
antiguru May 18, 2026
3b02085
doc/semantics: map Mz optimizer passes to Lean coverage
antiguru May 18, 2026
1159302
doc/semantics: filter_cross_pushdown_right
antiguru May 18, 2026
b261a40
doc/semantics: transforms.md, mark right pushdown shipped
antiguru May 18, 2026
893cb8c
doc/semantics: filter ∘ filter fusion
antiguru May 18, 2026
89e2236
doc/semantics: transforms.md, filter fusion shipped
antiguru May 18, 2026
6f8f367
doc/semantics: threshold_elision
antiguru May 18, 2026
d08e791
doc/semantics: project ∘ project fusion
antiguru May 18, 2026
7132f4d
doc/semantics: transforms.md, project fusion shipped
antiguru May 18, 2026
8448383
doc/semantics: demand, unused-column invariance for filter
antiguru May 18, 2026
005c4d9
doc/semantics: demand for project under IsPureData
antiguru May 18, 2026
deca34e
doc/semantics: transforms.md, demand shipped
antiguru May 18, 2026
c800d04
doc/semantics: filter idempotence (unconditional)
antiguru May 18, 2026
f742335
doc/semantics: filter commutativity (under err-freedom)
antiguru May 18, 2026
7f098a1
doc/semantics: transforms.md, idem + comm filter laws
antiguru May 18, 2026
fe3d7fc
doc/semantics: filter ∘ project pushdown at UnifiedStream level
antiguru May 18, 2026
6b64c22
doc/semantics: transforms.md, filter/project pushdown shipped
antiguru May 18, 2026
7a2d541
doc/semantics: clampToOne identity on .val 1/.error inputs
antiguru May 18, 2026
461a7be
doc/design: row-scoped diff encoding (non-absorbing err counts)
antiguru May 20, 2026
6f99f92
doc/semantics: ErrCount + Diff types (row-scoped retractable diff)
antiguru May 20, 2026
a54c8c0
doc/semantics: DiffWithGlobal absorbing layer
antiguru May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions ci/test/lean-semantics.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash

# Copyright Materialize, Inc. and contributors. All rights reserved.
#
# Use of this software is governed by the Business Source License
# included in the LICENSE file at the root of this repository.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0.
#
# lean-semantics.sh — build the Lean 4 semantics model in
# `doc/developer/semantics`. The Lean toolchain version is read from
# `lean-toolchain` and forwarded to the Dockerfile in the same
# directory, so the elan toolchain pin used by local developers and the
# CI image stay in lockstep.
#
# The Docker image is built locally on the agent and reused across CI
# runs by Docker's layer cache. There is no registry push; the image is
# cheap to rebuild from cold (≈ apt + elan + one Lean toolchain).

set -euo pipefail

cd "$(dirname "$0")/../.."

semantics_dir="doc/developer/semantics"
lean_toolchain="$(tr -d '[:space:]' < "$semantics_dir/lean-toolchain")"
image_tag="mz-lean-semantics:latest"

docker build \
--build-arg "LEAN_TOOLCHAIN=$lean_toolchain" \
--tag "$image_tag" \
-f "$semantics_dir/Dockerfile" \
"$semantics_dir"

docker run --rm \
--user "$(id -u):$(id -g)" \
-v "$PWD/$semantics_dir:/workspace" \
-w /workspace \
"$image_tag" \
lake build
13 changes: 13 additions & 0 deletions ci/test/pipeline.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,19 @@ steps:
coverage: skip
sanitizer: skip

- id: lean-semantics
label: ":lean: Lean semantics"
command: ci/test/lean-semantics.sh
inputs:
- doc/developer/semantics
- ci/test/lean-semantics.sh
depends_on: []
timeout_in_minutes: 15
agents:
queue: hetzner-x86-64-4cpu-8gb
coverage: skip
sanitizer: skip

- id: lint-macos
label: ":rust: macOS Clippy"
# Running on a manually installed macOS agent, so make sure we don't go out of disk
Expand Down
286 changes: 286 additions & 0 deletions doc/developer/design/20260517_error_handling_semantics.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions doc/developer/semantics/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Lake build cache.
.lake/
44 changes: 44 additions & 0 deletions doc/developer/semantics/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright Materialize, Inc. and contributors. All rights reserved.
#
# Use of this software is governed by the Business Source License
# included in the LICENSE file at the root of this repository.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0.
#
# Minimal Lean 4 toolchain image for building the Materialize semantics
# model in this directory. The Lean version is supplied via build arg,
# which `ci/test/lean-semantics.sh` reads from `lean-toolchain`, so the
# elan toolchain pin used by local developers and the CI image stay in
# lockstep.
#
# The image is intentionally small. It installs elan, a single Lean
# toolchain, and the system packages elan needs to download. No Mathlib,
# no editor, no test runners. Adding those is a deliberate future
# decision, not a passive consequence of inheriting a heavier base.

FROM ubuntu:26.04

ARG LEAN_TOOLCHAIN

# Install elan system-wide so the toolchain is usable by any uid that
# the container may be launched with (CI runs with the agent's uid via
# `--user`). Writes go to `/workspace/.lake/`, which is bind-mounted.
ENV ELAN_HOME=/opt/elan
ENV PATH=$ELAN_HOME/bin:$PATH

RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
&& rm -rf /var/lib/apt/lists/*

RUN curl --proto '=https' --tlsv1.2 -sSf \
https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
| sh -s -- --default-toolchain "$LEAN_TOOLCHAIN" -y --no-modify-path \
&& chmod -R a+rX "$ELAN_HOME" \
&& lean --version

WORKDIR /workspace
30 changes: 30 additions & 0 deletions doc/developer/semantics/Mz.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Mz.Datum
import Mz.Expr
import Mz.Eval
import Mz.Boolean
import Mz.MightError
import Mz.Strict
import Mz.Coalesce
import Mz.Laws
import Mz.Variadic
import Mz.ExprVariadic
import Mz.Bag
import Mz.ErrStream
import Mz.Pushdown
import Mz.ColRefs
import Mz.DiffSemiring
import Mz.DiffErrCount
import Mz.DiffWithGlobal
import Mz.UnifiedStream
import Mz.UnifiedConsolidate
import Mz.TimedConsolidate
import Mz.Aggregate
import Mz.Consolidate
import Mz.Triple
import Mz.Join
import Mz.JoinPushdown
import Mz.FilterFusion
import Mz.ProjectFusion
import Mz.Demand
import Mz.GroupBy
import Mz.SetOps
235 changes: 235 additions & 0 deletions doc/developer/semantics/Mz/Aggregate.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import Mz.Eval

/-!
# Aggregates

Strict aggregate reductions over `List Datum`. The spec rule is that
SQL aggregates such as `SUM`, `MIN`, `MAX`, and `AVG` propagate
`err` like strict scalar functions: any `err` input produces an
`err` output, with a deterministic choice of payload. `NULL` values
are skipped, mirroring PostgreSQL's `COUNT(expr)` and the
"`IGNORE NULLS`" reading of the other aggregates.

`COUNT` itself is `COUNT(*)` (count all rows) or `COUNT(expr)`
(count rows where `expr` is neither `NULL` nor `err`). The skeleton
models the latter via `aggCountNonNull`.

`try_sum`, `try_avg`, and friends — the non-strict variants that
swallow `err` into `NULL` instead of propagating — are future work.
They satisfy a *coalesce*-style law rather than a strict-propagation
law.
-/

namespace Mz

/-- `COUNT(expr)`: count rows whose value is neither `NULL` nor an
`err`. Matches the PostgreSQL aggregate. -/
def aggCountNonNull : List Datum → Nat
| [] => 0
| .bool _ :: rest => 1 + aggCountNonNull rest
| .int _ :: rest => 1 + aggCountNonNull rest
| .null :: rest => aggCountNonNull rest
| .err _ :: rest => aggCountNonNull rest

/-- Strict aggregate reduction. `err` propagates: the first `err`
encountered is returned. `NULL`s are skipped. The reducer `f` is
applied to the surviving values; an empty list (or a list of only
`NULL`s) returns `NULL`. -/
def aggStrict (f : Datum → Datum → Datum) : List Datum → Datum
| [] => .null
| .err e :: _ => .err e
| .null :: rest => aggStrict f rest
| x@(.bool _) :: rest =>
match aggStrict f rest with
| .err e => .err e
| .null => x
| r => f x r
| x@(.int _) :: rest =>
match aggStrict f rest with
| .err e => .err e
| .null => x
| r => f x r

/-! ## Strict propagation laws -/

/-- If any input is an `err`, the aggregate result is an `err`. The
exact payload is whichever `err` `aggStrict` selects (the first one
in scan order under this definition). -/
theorem aggStrict_err :
∀ {xs : List Datum} (f : Datum → Datum → Datum),
(∃ d ∈ xs, d.IsErr) → (aggStrict f xs).IsErr
| [], _, h => by
obtain ⟨_, hmem, _⟩ := h
cases hmem
| Datum.err e :: _, _, _ => by
show (Datum.err e).IsErr
trivial
| Datum.null :: rest, f, h => by
obtain ⟨d, hmem, hsafe⟩ := h
cases hmem with
| head _ => exact hsafe.elim
| tail _ h' =>
show (aggStrict f rest).IsErr
exact aggStrict_err f ⟨d, h', hsafe⟩
| Datum.bool b :: rest, f, h => by
obtain ⟨d, hmem, hsafe⟩ := h
cases hmem with
| head _ => exact hsafe.elim
| tail _ h' =>
have h_rest : (aggStrict f rest).IsErr :=
aggStrict_err f ⟨d, h', hsafe⟩
cases h_eval : aggStrict f rest with
| err e' =>
show (match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.bool b
| r => f (Datum.bool b) r).IsErr
rw [h_eval]; trivial
| null => rw [h_eval] at h_rest; exact h_rest.elim
| bool _ => rw [h_eval] at h_rest; exact h_rest.elim
| int _ => rw [h_eval] at h_rest; exact h_rest.elim
| Datum.int n :: rest, f, h => by
obtain ⟨d, hmem, hsafe⟩ := h
cases hmem with
| head _ => exact hsafe.elim
| tail _ h' =>
have h_rest : (aggStrict f rest).IsErr :=
aggStrict_err f ⟨d, h', hsafe⟩
cases h_eval : aggStrict f rest with
| err e' =>
show (match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.int n
| r => f (Datum.int n) r).IsErr
rw [h_eval]; trivial
| null => rw [h_eval] at h_rest; exact h_rest.elim
| bool _ => rw [h_eval] at h_rest; exact h_rest.elim
| int _ => rw [h_eval] at h_rest; exact h_rest.elim

/-- Dual: if no input is an `err`, the aggregate result is not an
`err`. The reducer `f` is assumed to preserve "no-err": applied to
two non-`err` values it produces a non-`err` value. -/
theorem aggStrict_no_err
(f : Datum → Datum → Datum)
(f_safe : ∀ x y, ¬x.IsErr → ¬y.IsErr → ¬(f x y).IsErr) :
∀ {xs : List Datum}, (∀ d ∈ xs, ¬d.IsErr) → ¬(aggStrict f xs).IsErr
| [], _ => by
show ¬(Datum.null).IsErr
intro h; cases h
| Datum.err _ :: _, h => by
exact (h _ (List.Mem.head _) trivial).elim
| Datum.null :: rest, h => by
show ¬(aggStrict f rest).IsErr
apply aggStrict_no_err f f_safe
intro d hmem; exact h d (List.Mem.tail _ hmem)
| Datum.bool b :: rest, h => by
have h_rest : ¬(aggStrict f rest).IsErr := by
apply aggStrict_no_err f f_safe
intro d hmem; exact h d (List.Mem.tail _ hmem)
have hb : ¬(Datum.bool b).IsErr := h _ (List.Mem.head _)
cases h_eval : aggStrict f rest with
| err e =>
rw [h_eval] at h_rest
exact absurd trivial h_rest
| null =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.bool b
| r => f (Datum.bool b) r).IsErr
rw [h_eval]; exact hb
| bool b' =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.bool b
| r => f (Datum.bool b) r).IsErr
rw [h_eval]
apply f_safe
· exact hb
· intro h_eq; cases h_eq
| int _ =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.bool b
| r => f (Datum.bool b) r).IsErr
rw [h_eval]
apply f_safe
· exact hb
· intro h_eq; cases h_eq
| Datum.int n :: rest, h => by
have h_rest : ¬(aggStrict f rest).IsErr := by
apply aggStrict_no_err f f_safe
intro d hmem; exact h d (List.Mem.tail _ hmem)
have hb : ¬(Datum.int n).IsErr := h _ (List.Mem.head _)
cases h_eval : aggStrict f rest with
| err e =>
rw [h_eval] at h_rest
exact absurd trivial h_rest
| null =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.int n
| r => f (Datum.int n) r).IsErr
rw [h_eval]; exact hb
| bool _ =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.int n
| r => f (Datum.int n) r).IsErr
rw [h_eval]
apply f_safe
· exact hb
· intro h_eq; cases h_eq
| int _ =>
show ¬(match aggStrict f rest with
| Datum.err e => Datum.err e
| Datum.null => Datum.int n
| r => f (Datum.int n) r).IsErr
rw [h_eval]
apply f_safe
· exact hb
· intro h_eq; cases h_eq

/-! ## Non-strict (`try_*`) variants

The proposed `try_sum`, `try_min`, etc. swallow `err` into `NULL`
instead of propagating. Defined here as a post-pass coalesce on
`aggStrict`'s output: an `err` result becomes `.null`; anything
else is unchanged. The skeleton's `evalCoalesce` exhibits the same
"`null` beats `err`" tiebreak, so an equivalent reading is

aggTry f xs = evalCoalesce [aggStrict f xs, .null]

The defining property is `aggTry_no_err`: the result is never an
`err`. The companion `aggTry_eq_aggStrict_of_no_err` says the
non-strict variant agrees with the strict one whenever the strict
one would not have erred — so an optimizer that has proved the
inputs error-free can swap `aggTry` for `aggStrict`. -/

def aggTry (f : Datum → Datum → Datum) (xs : List Datum) : Datum :=
match aggStrict f xs with
| Datum.err _ => Datum.null
| r => r

theorem aggTry_no_err (f : Datum → Datum → Datum) (xs : List Datum) :
¬(aggTry f xs).IsErr := by
show ¬(match aggStrict f xs with
| Datum.err _ => Datum.null
| r => r).IsErr
cases aggStrict f xs <;> intro h <;> cases h

theorem aggTry_eq_aggStrict_of_no_err
(f : Datum → Datum → Datum)
(f_safe : ∀ x y, ¬x.IsErr → ¬y.IsErr → ¬(f x y).IsErr)
{xs : List Datum} (h : ∀ d ∈ xs, ¬d.IsErr) :
aggTry f xs = aggStrict f xs := by
have h_safe : ¬(aggStrict f xs).IsErr := aggStrict_no_err f f_safe h
show (match aggStrict f xs with
| Datum.err _ => Datum.null
| r => r) = aggStrict f xs
cases h_eval : aggStrict f xs with
| err _ => exact absurd (h_eval ▸ h_safe) (fun h' => h' trivial)
| null => rfl
| bool _ => rfl
| int _ => rfl

end Mz
Loading
Loading