Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions docs/docs/language/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ Functions that take other functions as arguments.
| `scan-left` | variadic | Left-to-right running fold | `(scan-left + (enlist 1 2 3))` → `[1 3 6]` |
| `scan-right` | variadic | Right-to-left running fold | `(scan-right + (enlist 1 2 3))` → `[6 5 3]` |
| `apply` | variadic | Zip-apply function pairwise over two lists | `(apply + (enlist 1 2) (enlist 3 4))` → `[4 6]` |
| `map-left` | variadic | Map with left argument fixed | `(map-left + 10 [1 2 3])` → `[11 12 13]` |
| `map-right` | variadic | Map with right argument fixed | `(map-right - [10 20 30] 5)` → `[5 15 25]` |
| `map-left` | variadic | Map each element of the left over the whole right | `(map-left + 10 [1 2 3])` → `[11 12 13]` |
| `map-right` | variadic | Map the whole left over each element of the right | `(map-right - [10 20 30] 5)` → `[5 15 25]` |

## Collection Operations

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/queries/joins.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ Window join with per-row time intervals. `intervals` is a list of two vectors `[
[928 528 648 914 918 626 577 817 620 698])))

; Build per-row intervals: [lo, hi] for each trade
(set intervals (map-left + [-1000 1000] (at trades 'Time)))
(set intervals (map-right + [-1000 1000] (at trades 'Time)))

; Window join: min bid and max ask within each window
(window-join [Sym Time] intervals trades quotes
Expand All @@ -202,7 +202,7 @@ Window join with per-row time intervals. `intervals` is a list of two vectors `[
(list bsym btime bid ask)))

; Build intervals from the trades timestamp
(set intervals (map-left + [-1000 1000] (at trades 'Ts)))
(set intervals (map-right + [-1000 1000] (at trades 'Ts)))

(window-join [Sym Ts] intervals trades quotes
{bid: (min Bid) ask: (max Ask)})
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/all-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ Functions that take other functions as arguments for mapping, folding, and filte
| `scan-right` | variadic | — | Right-to-left running fold | `(scan-right + (enlist 1 2 3))` → `[6 5 3]` |
| `filter` | binary | — | Keep elements where boolean mask is true | `(filter [1 2 3 4] (> [1 2 3 4] 2))` → `[3 4]` |
| `apply` | variadic | — | Zip-apply function pairwise over lists | `(apply + (enlist 1 2) (enlist 3 4))` → `[4 6]` |
| `map-left` | variadic | — | Map with left argument fixed | `(map-left + 10 [1 2 3])` → `[11 12 13]` |
| `map-right` | variadic | — | Map with right argument fixed | `(map-right - [10 20 30] 5)` → `[5 15 25]` |
| `map-left` | variadic | — | Map each element of the left over the whole right | `(map-left + 10 [1 2 3])` → `[11 12 13]` |
| `map-right` | variadic | — | Map the whole left over each element of the right | `(map-right - [10 20 30] 5)` → `[5 15 25]` |

```lisp
; Transform each row with map
Expand Down
4 changes: 2 additions & 2 deletions src/lang/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1331,8 +1331,8 @@ ray_t* ray_table_fn(ray_t* names, ray_t* cols) {
{ ray_release(tbl); if (_bxn) ray_release(_bxn); if (_bxc) ray_release(_bxc); return ray_error("domain", NULL); }

/* Empty generic list → typeless empty column: keep it as a RAY_LIST
* so its storage type is adopted from the first inserted value
* (q-style () column), rather than defaulting to I64. */
* so its storage type is adopted from the first inserted value,
* rather than defaulting to I64. */
if (nrows == 0) {
ray_retain(col_src);
tbl = ray_table_add_col(tbl, name_id, col_src);
Expand Down
30 changes: 16 additions & 14 deletions src/ops/collection.c
Original file line number Diff line number Diff line change
Expand Up @@ -2200,26 +2200,28 @@ static ray_t* map_iterate(ray_t* fn, ray_t* fixed, ray_t* vec, int fixed_is_left
return out;
}

/* (map-left fn left right) → fix the LEFT arg, iterate over the right:
* apply fn(left, right_i) for each element of right. If right is scalar this
* collapses to a single fn(left, right) (handled by map_iterate). */
/* (map-left fn left right): iterate the LEFT operand, pairing each element with
* the whole RIGHT → fn(left_i, right).
* (map-right fn left right): iterate the RIGHT operand, pairing the whole LEFT
* with each element → fn(left, right_i).
*
* The iterated side is fixed by the operator, not auto-detected. When that side
* is an atom there is nothing to iterate, so fn is applied once to (left,right);
* for an atomic fn that single call is exactly its broadcast over the other
* operand, so map-left/map-right agree with plain atomic application there.
* Every call goes through call_fn2, which routes atomic builtins through the
* broadcasting engine (so a held vector conforms element-wise) and hands
* lambdas their arguments whole. */
ray_t* ray_map_left_fn(ray_t** args, int64_t n) {
if (n != 3) return ray_error("domain", NULL);
ray_t* fn = args[0];
ray_t* left = args[1];
ray_t* right = args[2];
return map_iterate(fn, left, right, 1); /* fn(left, right_i) */
/* iterate left (vec), hold right (fixed) → fn(left_i, right) */
return map_iterate(args[0], args[2], args[1], 0);
}

/* (map-right fn left right) → fix the RIGHT arg, iterate over the left:
* apply fn(left_i, right) for each element of left. If left is scalar this
* collapses to a single fn(left, right) (handled by map_iterate). */
ray_t* ray_map_right_fn(ray_t** args, int64_t n) {
if (n != 3) return ray_error("domain", NULL);
ray_t* fn = args[0];
ray_t* left = args[1];
ray_t* right = args[2];
return map_iterate(fn, right, left, 0); /* fn(left_i, right) */
/* iterate right (vec), hold left (fixed) → fn(left, right_i) */
return map_iterate(args[0], args[1], args[2], 1);
}

/* ══════════════════════════════════════════
Expand Down
2 changes: 1 addition & 1 deletion src/ops/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -9198,7 +9198,7 @@ ray_t* ray_xbar_fn(ray_t* col, ray_t* bucket) {
* ══════════════════════════════════════════ */

/* Derive the storage type for a typeless (empty RAY_LIST) column from the
* first value inserted into it — q-style () columns adopt their type on the
* first value inserted into it — an empty () column adopts its type on the
* first insert. Returns the RAY_* column type, or RAY_LIST when the payload
* is itself nested (non-atom elements → a genuine list column). */
static int8_t typeless_col_type(ray_t* payload) {
Expand Down
25 changes: 19 additions & 6 deletions test/rfl/collection/cov2.rfl
Original file line number Diff line number Diff line change
Expand Up @@ -201,30 +201,43 @@
(count (binr [1 3 5] [0 2 4])) -- 3

;; ════════════════════════════════════════════════════════════════
;; 13. map-left (lines 2108-2121)
;; 13. map-left: iterate the LEFT, hold the right whole, calling fn(left_i,
;; right). An atom on the left iterates once, which for an atomic fn is
;; identical to plain broadcast application.
;; ════════════════════════════════════════════════════════════════
;; fn fixed vec: fn(fixed, elem) for each elem
(map-left + 10 (list 1 2 3)) -- (list 11 12 13)
(map-left * 3 (list 2 4 6)) -- (list 6 12 18)
;; map-left arity error
(try (map-left + 1) (fn [e] "err")) -- "err"

;; map-left auto-detect: vec is scalar, fixed is vector → swap roles
;; Iterate the left vector, holding the right scalar.
(map-left + (list 1 2 3) 5) -- (list 6 7 8)
(map-left - (list 10 20 30) 5) -- (list 5 15 25)
;; Non-broadcasting lambda exposes the structure builtins hide. Iterating the
;; left vector yields one cell per element; an atom left applies fn once.
(map-left (fn [x y] (list x y)) [1 2 3] 10) -- (list [1 10] [2 10] [3 10])
(map-left (fn [x y] (list x y)) 10 [1 2 3]) -- (list 10 [1 2 3])

;; ════════════════════════════════════════════════════════════════
;; 14. map-right (lines 2125-2138)
;; 14. map-right: iterate the RIGHT, hold the left whole, calling fn(left,
;; right_i). An atom on the right iterates once, which for an atomic fn is
;; identical to plain broadcast application.
;; ════════════════════════════════════════════════════════════════
;; fn vec fixed: fn(elem, fixed) for each elem
(map-right - (list 10 20 30) 3) -- (list 7 17 27)
(map-right + (list 1 2 3) 100) -- (list 101 102 103)
;; map-right arity error
(try (map-right + 1) (fn [e] "err")) -- "err"

;; map-right auto-detect: vec is scalar, fixed is vector → iterate fixed
;; Iterate the right vector, holding the left scalar.
(map-right + 5 (list 1 2 3)) -- (list 6 7 8)
(map-right - 100 (list 1 2 3)) -- (list 99 98 97)
;; Non-broadcasting lambda: iterating the right vector pairs the whole left with
;; each element (fn order is source order); an atom right applies fn once.
(map-right (fn [x y] (list x y)) 10 [1 2 3]) -- (list [10 1] [10 2] [10 3])
(map-right (fn [x y] (list x y)) [1 2 3] 10) -- (list [1 2 3] 10)
;; concat is non-broadcasting: iterating the right yields one concat(10,e) per
;; element ([10 e]); a single concat(10,[1 2 3]) would instead flatten.
(map-right concat 10 [1 2 3]) -- (list [10 1] [10 2] [10 3])

;; ════════════════════════════════════════════════════════════════
;; 15. map-iterate scalar path (line 2070-2075):
Expand Down
Loading
Loading