linkrs_core/function.rs
1//! Function-name classification for plan analysis.
2//!
3//! Identifies functions whose arguments are evaluated per element of a
4//! list (lambda-style evaluation). Plan passes that reason about
5//! evaluation order (for example factorization flattening) consult this
6//! module instead of hardcoding function names, so the classification
7//! stays consistent wherever it is used.
8
9/// Names evaluated per list element with the lambda at argument index 1.
10///
11/// `list_extract` takes a list and an index rather than a lambda, but it
12/// has historically received the same per-element treatment; that
13/// treatment is preserved here to avoid changing flattening behavior.
14/// `list_any`, `list_all` and `list_single` are predicate variants that
15/// evaluate the lambda per element like `list_filter`.
16const LIST_LAMBDA_NAMES: [&str; 7] = [
17 "list_filter",
18 "list_extract",
19 "list_transform",
20 "list_reduce",
21 "list_any",
22 "list_all",
23 "list_single",
24];
25
26/// Report whether a function evaluates an argument per list element.
27///
28/// Matching is case-insensitive to mirror binder normalization.
29pub fn is_list_lambda(name: &str) -> bool {
30 LIST_LAMBDA_NAMES.contains(&name.to_lowercase().as_str())
31}
32
33/// Argument position of the per-element expression for a list lambda.
34///
35/// Returns `None` for functions without per-element evaluation.
36pub fn list_lambda_arg_index(name: &str) -> Option<usize> {
37 if is_list_lambda(name) {
38 Some(1)
39 } else {
40 None
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn known_lambdas_match_case_insensitively() {
50 assert!(is_list_lambda("list_filter"));
51 assert!(is_list_lambda("LIST_TRANSFORM"));
52 assert!(is_list_lambda("List_Extract"));
53 assert!(is_list_lambda("list_reduce"));
54 assert!(is_list_lambda("list_any"));
55 assert!(is_list_lambda("LIST_ALL"));
56 assert!(is_list_lambda("List_Single"));
57 }
58
59 #[test]
60 fn eager_functions_do_not_match() {
61 assert!(!is_list_lambda("list_append"));
62 assert!(!is_list_lambda("list_contains"));
63 assert!(!is_list_lambda("abs"));
64 assert!(!is_list_lambda(""));
65 }
66
67 #[test]
68 fn lambda_arg_index_is_second_position() {
69 assert_eq!(list_lambda_arg_index("list_filter"), Some(1));
70 assert_eq!(list_lambda_arg_index("list_transform"), Some(1));
71 assert_eq!(list_lambda_arg_index("list_append"), None);
72 }
73}