Skip to content

Commit fa307f0

Browse files
committed
better docs
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent 30fdd01 commit fa307f0

1 file changed

Lines changed: 23 additions & 48 deletions

File tree

‎vortex-array/src/scalar_fn/vtable.rs‎

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -194,68 +194,43 @@ pub trait ScalarFnVTable: 'static + Sized + Clone + Send + Sync {
194194

195195
/// Returns whether this scalar function is strict.
196196
///
197-
/// A strict function propagates nulls: at each row, a null in any argument always produces a
198-
/// null result:
197+
/// A strict function returns null for a row when any argument is null for that row. This
198+
/// matches [PostgreSQL's `STRICT` convention](https://www.postgresql.org/docs/current/sql-createfunction.html)
199+
/// for null propagation.
199200
///
200-
/// ```text
201-
/// vj = NULL for some j => f(v1, .., vk) = NULL
202-
/// ```
201+
/// Return `true` only when this holds for every argument. `add` is strict, but Kleene `AND`
202+
/// is not because `false AND null` returns `false`. `is_null` is also not strict.
203203
///
204-
/// Strictness does not guarantee that non-null inputs produce a non-null result. For example,
205-
/// `list_sum` returns null for a valid but empty list. Therefore, strictness only bounds the
206-
/// output validity:
204+
/// Strictness does not require valid inputs to produce a valid output. For example,
205+
/// [`crate::expr::list_sum`] returns null for a valid empty list. Implement
206+
/// [`ScalarFnVTable::validity`] only when the output validity can be derived without
207+
/// evaluation.
207208
///
208-
/// ```text
209-
/// valid(f(a1, .., ak)) ⊆ valid(a1) ∧ .. ∧ valid(ak)
210-
/// ```
209+
/// [`ScalarFnVTable::return_dtype`] must return a nullable output dtype when any input dtype is
210+
/// nullable. A `cast` that forces a non-nullable output dtype is therefore not strict.
211211
///
212-
/// Many strict functions satisfy the stronger equality, where output validity is exactly the
213-
/// intersection of the input validities. They can advertise that through
214-
/// [`ScalarFnVTable::validity`], but callers must not infer it from strictness alone.
215-
///
216-
/// Optimizations use strictness to move a mask through the function:
217-
///
218-
/// ```text
219-
/// f(a1, .., mask(aj', m), .., ak) == mask(f(a1, .., aj', .., ak), m)
220-
/// ```
221-
///
222-
/// The per-argument requirement matters when, for example, pushing a function through
223-
/// dictionary codes while leaving its other arguments unmasked. Kleene `AND` and `OR` are not
224-
/// strict even though a mask can be moved when applied to all arguments together:
225-
/// `false AND null = false`.
226-
///
227-
/// Returning `true` also requires [`ScalarFnVTable::return_dtype`] to propagate nullability:
228-
/// if any input dtype is nullable, the output dtype must be nullable. For example, `cast` is
229-
/// value-strict but can pin a non-nullable output dtype through its options, so it must return
230-
/// `false`.
231-
///
232-
/// This method describes only the scalar function, not its child expressions. Nullary
212+
/// This property applies only to the scalar function, not its child expressions. Nullary
233213
/// functions are vacuously strict. The default is conservatively `false`.
234214
fn is_strict(&self, options: &Self::Options) -> bool {
235215
_ = options;
236216
false
237217
}
238218

239-
/// Returns whether this expression is semantically fallible. Conservatively defaults to
240-
/// `true`.
219+
/// Returns whether this scalar function can raise a semantic error.
241220
///
242-
/// An expression is semantically fallible if there exists a set of well-typed inputs that
243-
/// causes the expression to produce an error as part of its _defined behavior_. For example,
244-
/// `checked_add` is fallible because integer overflow is a domain error, and division is
245-
/// fallible because of division by zero.
221+
/// Return `true` if a well-typed call can error because of its values. `checked_add` is
222+
/// fallible on integer overflow, and integer division is fallible when its divisor is zero.
223+
/// A null result is not an error: [`crate::expr::list_sum`] is infallible for an empty list.
246224
///
247-
/// This does **not** include execution errors that are incidental to the implementation, such
248-
/// as canonicalization failures, memory allocation errors, or encoding mismatches. Those can
249-
/// happen to any expression and are not what this method captures.
225+
/// Exclude incidental execution errors, such as canonicalization failures, allocation errors,
226+
/// and encoding mismatches. They are not part of the function's semantics.
250227
///
251-
/// This property is used by optimizations that speculatively evaluate an expression over values
252-
/// that may not appear in the actual input. For example, pushing a scalar function down to a
253-
/// dictionary's values array is only safe when the function is infallible or all values are
254-
/// referenced, since a fallible function might error on a value left unreferenced after
255-
/// slicing that would never be encountered during normal evaluation.
228+
/// Returning `false` permits optimizations that evaluate the function over values that no input
229+
/// row references. Dictionary push-down, for example, evaluates every dictionary value, so a
230+
/// fallible function could error on a value that row-wise evaluation would never reach.
256231
///
257-
/// Note: this is only applicable to expressions that pass type-checking via
258-
/// [`ScalarFnVTable::return_dtype`].
232+
/// This applies only to the scalar function, not its child expressions, and only to inputs
233+
/// accepted by [`ScalarFnVTable::return_dtype`]. The default is conservatively `true`.
259234
fn is_fallible(&self, options: &Self::Options) -> bool {
260235
_ = options;
261236
true

0 commit comments

Comments
 (0)