From fb0d0b91bfd45d1911e993ca753d81da5e60f60c Mon Sep 17 00:00:00 2001 From: tslil Date: Sat, 21 Mar 2026 15:10:05 +0000 Subject: fixing evaluation semantics --- rprt-engine/src/evaluate.rs | 33 ++++++++++++++++++++++++++++++--- rprt.md | 30 +++++++++++++++--------------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/rprt-engine/src/evaluate.rs b/rprt-engine/src/evaluate.rs index f6fee8a..b23c1be 100644 --- a/rprt-engine/src/evaluate.rs +++ b/rprt-engine/src/evaluate.rs @@ -11,6 +11,8 @@ use thiserror::Error; pub enum EvaluationError { #[error("{0}")] SelectionFunctionError(VectoriseError), + #[error("Invalid right-only application")] + IROApplication, #[error("Not yet implemented {0}")] UnimplementedError(&'static str), } @@ -21,6 +23,10 @@ pub fn evaluate( left: Option, right: Option, ) -> Result { + if left.is_none() && right.is_some() { + return Err(EvaluationError::IROApplication); + }; + match comp { Composite::SelectionFunction { func, @@ -36,8 +42,29 @@ pub fn evaluate( Err(EvaluationError::UnimplementedError("text function")) } Composite::Hook { kind, left, right } => Err(EvaluationError::UnimplementedError("hook")), - Composite::Train2 { f, g } => Err(EvaluationError::UnimplementedError("train2")), - Composite::Train3 { f, g, h } => Err(EvaluationError::UnimplementedError("train3")), - Composite::Group { operations } => Err(EvaluationError::UnimplementedError("group")), + Composite::Train2 { f, g } => { + let (left, mut state) = evaluate(es, *f, left, right)?; + let (sel, more_state) = evaluate(es, *g, Some(left), None)?; + state.extend(more_state); + Ok((sel, state)) + } + Composite::Train3 { f, g, h } => { + let (f_left, mut f_state) = evaluate(es, *f, left.clone(), right.clone())?; + let (g_right, h_state) = evaluate(es, *h, left, right)?; + let (sel, g_state) = evaluate(es, *g, Some(f_left), Some(g_right))?; + f_state.extend(h_state); + f_state.extend(g_state); + Ok((sel, f_state)) + } + Composite::Group { operations } => { + // TODO: does rust have some monadic failure map thing on first failure? + let mut selections = Vec::new(); + let mut states = Vec::new(); + for f in operations { + let (sel, state) = evaluate(es, f, left.clone(), right.clone())?; + selections.extend(sel); + states.extend(state); + } + } } } diff --git a/rprt.md b/rprt.md index 12449ca..31349dd 100644 --- a/rprt.md +++ b/rprt.md @@ -75,32 +75,32 @@ The buffer axis (rank 3) is the **leading axis**: operations on rank 3 selection A **train** is a sequence of functions that compose according to specific rules, following [APL train syntax](https://aplwiki.com/wiki/Train). **2-train (Atop)** `F G`: -- Niladic: `(F G) = F (G)` -- Monadic: `(F G) ω = F (G ω)` -- Dyadic: `α (F G) ω = F (α G ω)` +- Niladic: `F G` = (F) G`; `F` is evaluated niladically and `G` is evaluated monadically on the result +- Monadic: `ω (F G) = (ω F) G` +- Dyadic: `ω (F G) α = (ω F α) G` **3-train (Fork)** `F G H`: -- Niladic: `(F G H) = (F) G (H)` -- Monadic: `(F G H) ω = (F ω) G (H ω)` -- Dyadic: `α (F G H) ω = (α F ω) G (α H ω)` +- Niladic: `F G H` = `(F) G (H)`; `F`, `H` are evaluated niladically and `G` is evaluated dyadically on the result +- Monadic: `ω (F G H) = (ω F) G (ω H)` +- Dyadic: `ω (F G H) α = (ω F α) G (ω H α)` -**Longer trains**: Parsed right-associatively. A train of n functions is parsed by taking the rightmost 3 functions as a fork (if n is odd and ≥3) or the rightmost 2 functions as atop (if n is even), then recursively parsing the remaining functions as the left part. For example: -- 4 functions `F G H I` → `F (G H I)` (atop of F with a fork) -- 5 functions `F G H I J` → `(F G) (H I J)` (atop of an atop with a fork) +**Longer trains**: Parsed left-associatively. A train of n functions is parsed by taking the leftmost 3 functions as a fork (if n is odd and ≥3) or the leftmost 2 functions as atop (if n is even), then recursively parsing the remaining functions as the right part. For example: +- 4 functions `F G H I` → `(F G H) I` (I atop a fork) +- 5 functions `F G H I J` → `(F G H) (I J)` (atop, atop a fork) ### Hooks RPRT provides two explicit hook combinators from BQN for flexible function composition: **Before (Left Hook)** `F>G`: -- Niladic: `(F>G) = (F) G` -- Monadic: `(F>G) ω = (F ω) G ω` -- Dyadic: `α (F>G) ω = (F α) G ω` +- Niladic: `F>G = (F) G`; `F` is evaluated niladically and `G` is evaluated monadically on the result +- Monadic: `ω (F>G) = (ω F) G ω` +- Dyadic: `ω (F>G) α = (ω F) G α` **After (Right Hook)** `F