Refactor PushableSeries trait

Introduce a new trait `PushableSeries` to distinguish objects that can
have values pushed into them from those that are only readable series.
This clarifies the API and allows for more precise type checking.

The `Object` trait no longer has a default `push_value` implementation.
Instead, `PushableSeries` now provides this functionality.
`SeriesMember` has also been updated to require `PushableSeries` for
RecordSeries field buffers.
This commit is contained in:
2026-03-22 19:45:50 +01:00
parent 843ee7dfed
commit ae1d94e507
2 changed files with 50 additions and 25 deletions
+37 -16
View File
@@ -5,7 +5,7 @@ use std::rc::Rc;
use std::sync::Arc;
use crate::ast::environment::Environment;
use crate::ast::types::{Keyword, Object, RecordLayout, Series, StaticType, Value};
use crate::ast::types::{Keyword, Object, PushableSeries, RecordLayout, Series, StaticType, Value};
use crate::ast::types::{Purity, Signature};
// ============================================================================
@@ -144,14 +144,20 @@ impl<T: ScalarValue> Object for ScalarSeries<T> {
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
fn as_pushable_series(&self) -> Option<&dyn PushableSeries> {
Some(self)
}
}
impl<T: ScalarValue> PushableSeries for ScalarSeries<T> {
fn push_value(&self, value: Value) {
if let Some(v) = T::from_value(value) {
self.data.borrow_mut().push(v);
}
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
}
impl<T: ScalarValue> Series for ScalarSeries<T> {
@@ -200,12 +206,18 @@ impl Object for ValueSeries {
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn push_value(&self, value: Value) {
self.data.borrow_mut().push(value);
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
fn as_pushable_series(&self) -> Option<&dyn PushableSeries> {
Some(self)
}
}
impl PushableSeries for ValueSeries {
fn push_value(&self, value: Value) {
self.data.borrow_mut().push(value);
}
}
impl Series for ValueSeries {
@@ -224,10 +236,10 @@ impl Series for ValueSeries {
// 3. RecordSeries (SoA - Struct of Arrays)
// ============================================================================
/// Marker supertrait combining `Object` and `Series` for RecordSeries field buffers.
/// Ensures each field buffer can be stored as a typed series AND used as a dynamic object
/// (including `push_value` via the `Object` supertrait).
pub trait SeriesMember: Object + Series {}
/// Marker supertrait for RecordSeries field buffers.
/// Ensures each field buffer supports indexed access (Series), dynamic dispatch (Object),
/// and value insertion (PushableSeries).
pub trait SeriesMember: PushableSeries {}
impl SeriesMember for ScalarSeries<f64> {}
impl SeriesMember for ScalarSeries<i64> {}
@@ -321,6 +333,15 @@ impl Object for RecordSeries {
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
fn as_pushable_series(&self) -> Option<&dyn PushableSeries> {
Some(self)
}
}
impl PushableSeries for RecordSeries {
fn push_value(&self, value: Value) {
if let Value::Record(_, values) = value {
for (i, v) in values.iter().enumerate() {
@@ -332,9 +353,6 @@ impl Object for RecordSeries {
panic!("push to RecordSeries expects a record");
}
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
}
impl Series for RecordSeries {
@@ -622,8 +640,11 @@ pub fn register(env: &Environment) {
let (series_val, val) = (&args[0], &args[1]);
if let Value::Object(obj) = series_val {
obj.push_value(val.clone());
return Value::Void;
if let Some(p) = obj.as_pushable_series() {
p.push_value(val.clone());
return Value::Void;
}
panic!("push expects a pushable series, got: {}", obj.type_name());
}
panic!("push expects a series object as the first argument")
},