Skip to content

v2.1.6#288

Merged
singaraiona merged 7 commits into
masterfrom
dev
Jun 23, 2026
Merged

v2.1.6#288
singaraiona merged 7 commits into
masterfrom
dev

Conversation

@singaraiona

Copy link
Copy Markdown
Collaborator

No description provided.

The GitHub Pages deploy (.github/workflows/pages.yml) installs docs deps
from requirements-docs.txt and uses it as the setup-python pip cache key,
but commit 2cb1441 ("chore") deleted the file. The deploy has failed at
the setup-python step ever since ("No file matched to [requirements-docs.txt
or **/pyproject.toml]") — the latest dev→master merge just surfaced it.

Restore the file with mkdocs-material (which bundles mkdocs, pymdown-extensions,
and the material emoji extensions used in mkdocs.yml). The previously listed
mkdocs-redirects is intentionally omitted — mkdocs.yml documents that plugin
as disabled (only `search` is active).
requirements-docs.txt is CI-only; at repo root it's clutter, and it can't
live under docs/ (that's mkdocs docs_dir, so it would be published into the
site verbatim). Move it next to the workflow that consumes it and update
pages.yml's path trigger, pip cache key, and install step accordingly.
`within` worked as a standalone vector op but the DAG WHERE compiler
rejected it (`compile_expr_dag` is a whitelist and it was never wired),
so `(select {... where: (within col [lo hi])})` errored with
"WHERE predicate not supported by DAG compiler" while `in` was accepted.

Lower `(within col [lo hi])` to `(and (>= col lo) (<= col hi))`, reusing
the existing comparison constructors. The range bounds are extracted from
the constant range vector via ray_at_fn (preserving DATE/TIME/I32 element
type) and wrapped as const-atom nodes. Because it compiles to plain
comparison opcodes, `within` now inherits range-index rowsel, partition
pruning, null handling, type promotion and the selection-aware AND-chain
path for free.

Parity with `in`: the range must be a compile-time-constant 2-element
vector (same literal-operand constraint OP_IN's set has); a non-const or
wrong-length range falls back to the eval-level builtin.

Closes #284
`.ipc.open` had no way to bound a connect. The blocking connect() ignores
SO_RCVTIMEO/SO_SNDTIMEO, so a dead or packet-filtered peer would hang for
the OS default (often minutes) with no user control.

Make `.ipc.open` variadic with an optional trailing timeout (milliseconds):

    (.ipc.open "host:port")
    (.ipc.open "host:port" 2000)

The timeout bounds both the TCP connect and the handshake I/O:
- ray_sock_connect now drives a non-blocking connect + poll(POLLOUT) when
  a positive timeout is given, restoring blocking mode for the handshake;
  on timeout it sets errno=ETIMEDOUT (added ray_sock_set_blocking helper).
- ray_ipc_connect takes a timeout_ms, defaulting to the long-standing 5s
  budget when none is supplied, and maps a connect timeout to a new -5
  return code so .ipc.open reports "connection timed out" distinctly from
  "connection refused".

ray_hopen_fn is now a vary builtin validating arity (rank), timeout type
(type) and sign (domain). Updated the public/internal prototypes, the
.repl.connect wrapper, the C unit tests pinning the signature, and the
IPC docs.

Closes #286
A server-side function returning (first v) / (last v) over IPC never
replied — the client blocked forever (issue #285).

Root cause: first/last (and sum/min/max/avg) over an I64/F64 vector build
a deferred-DAG *lazy* result via AGG_VEC_VIA_DAG → ray_lazy_wrap. In
eval_payload_core, STR payloads go through ray_eval_str (which
materializes) but expression-list payloads go through ray_eval, which
returns the lazy object verbatim. ray_serde_size(lazy) is <= 0, and
send_response did `if (ser_size <= 0) return;` — silently sending nothing,
leaving the client waiting on a reply that never came.

Fixes, both in src/core/ipc.c:
- eval_payload_core now materializes a lazy result (ray_lazy_materialize)
  before it reaches the wire — covers both server send paths.
- send_response substitutes a serializable error instead of dropping an
  unserializable reply, so any future non-serializable value surfaces as a
  clean client-side error rather than a silent infinite hang.

Regression test test/rfl/system/ipc_first_last.rfl covers first/last (and
sum/min/max) returned via expression-list payloads and via a server-side
lambda, exactly as reported.

Closes #285
`(try expr handler)` previously required the handler to be a function and
raised `try: handler must be a function` (interpreter) / `apply: head is
not callable` (compiled) for any other 2nd argument. Because Rayfall
lambdas do not capture closures, there was no way to surface an outer
binding from the failure branch — `(fn [e] outer)` cannot see `outer`.

Now the 2nd argument is dispatched on the type of its evaluated value:
a 1-arg-callable (LAMBDA or UNARY builtin) is invoked with the error
value as before; ANY other value is returned as-is as a fallback. The
handler slot is an ordinary expression evaluated in the current scope, so
the fallback can be a literal, a list expression, or an outer variable:

    ((fn [data] (try (raise "Oops") data)) 123)   ; => 123
    (try (raise 1) (list 1 2 3))                  ; => [1 2 3]
    (try (raise 1) "")                            ; => ""

A single shared helper, ray_try_handle, makes the call-vs-fallback
decision; both execution paths route through it — the interpreter
(ray_try_fn) and the compiled VM via a new OP_TRYH opcode that replaces
the unconditional OP_CALLF in the compiled `try`. No per-expression
special-casing.

Updates try_raise.rfl / eval_branch_cov.rfl (which previously locked in
the reject behavior) and the control-flow / function-reference docs.
@singaraiona singaraiona merged commit 49f0105 into master Jun 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant