Repository: pola-rs/polars
File: crates/polars-expr/src/expressions/mod.rs (Rust)

Code before:
        let groups: Cow<'static, GroupPositions> = Cow::Owned(groups);
        AggregationContext {
            state: self.state.clone(),
            groups,
            update_groups: self.update_groups,
            original_len: self.original_len,
        }
    }
}

/// Take a DataFrame and evaluate the expressions.
/// Implement this for Column, lt, eq, etc
pub trait PhysicalExpr: Send + Sync {
    fn as_expression(&self) -> Option<&Expr> {
        None
    }

    fn as_column(&self) -> Option<PlSmallStr> {
        None
    }

    /// Take a DataFrame and evaluate the expression.
    fn evaluate(&self, df: &DataFrame, state: &ExecutionState) -> PolarsResult<Column> {
        self.evaluate_impl(df, state).map_err(|e| {
            if let Some(expr) = self.as_expression() {
                e.with_expr_context(expr.to_string().into())
            } else {
                e
            }
        })
    }

    fn evaluate_impl(&self, df: &DataFrame, _state: &ExecutionState) -> PolarsResult<Column>;

    /// Some expression that are not aggregations can be done per group
    /// Think of sort, slice, filter, shift, etc.
    /// defaults to ignoring the group
    ///
    /// This method is called by an aggregation function.
    ///
    /// In case of a simple expr, like 'column', the groups are ignored and the column is returned.
    /// In case of an expr where group behavior makes sense, this method is called.
    /// For a filter operation for instance, a Series is created per groups and filtered.
    ///
    /// An implementation of this method may apply an aggregation on the groups only. For instance
    /// on a shift, the groups are first aggregated to a `ListChunked` and the shift is applied per
    /// group. The implementation then has to return the `Series` exploded (because a later aggregation
    /// will use the group tuples to aggregate). The group tuples also have to be updated, because
    /// aggregation to a list sorts the exploded `Series` by group.
    ///
    /// This has some gotcha's. An implementation may also change the group tuples instead of

Review comment (suggestion):
Should add a note to the function comment saying to avoid implementing this directly and instead implement `evaluate_impl`, as `evaluate()` adds error tracing.


Code after:
        let groups: Cow<'static, GroupPositions> = Cow::Owned(groups);
        AggregationContext {
            state: self.state.clone(),
            groups,
            update_groups: self.update_groups,
            original_len: self.original_len,
        }
    }
}

/// Take a DataFrame and evaluate the expressions.
/// Implement this for Column, lt, eq, etc
pub trait PhysicalExpr: Send + Sync {
    fn as_expression(&self) -> Option<&Expr> {
        None
    }

    fn as_column(&self) -> Option<PlSmallStr> {
        None
    }

    /// Take a DataFrame and evaluate the expression.
    ///
    /// Note: implementers should implement evaluate_impl instead, as this wraps
    /// that call with an error context.
    fn evaluate(&self, df: &DataFrame, state: &ExecutionState) -> PolarsResult<Column> {
        self.evaluate_impl(df, state).map_err(|e| {
            if let Some(expr) = self.as_expression() {
                e.with_expr_context(expr.to_string().into())
            } else {
                e
            }
        })
    }

    fn evaluate_impl(&self, df: &DataFrame, _state: &ExecutionState) -> PolarsResult<Column>;

    /// Some expression that are not aggregations can be done per group
    /// Think of sort, slice, filter, shift, etc.
    /// defaults to ignoring the group
    ///
    /// This method is called by an aggregation function.
    ///
    /// In case of a simple expr, like 'column', the groups are ignored and the column is returned.
    /// In case of an expr where group behavior makes sense, this method is called.
    /// For a filter operation for instance, a Series is created per groups and filtered.
    ///
    /// An implementation of this method may apply an aggregation on the groups only. For instance
    /// on a shift, the groups are first aggregated to a `ListChunked` and the shift is applied per
    /// group. The implementation then has to return the `Series` exploded (because a later aggregation
    /// will use the group tuples to aggregate). The group tuples also have to be updated, because