Closed RolandMacDoland closed 1 week ago
@dominikandreasseitz @kaosmicadei Here is a very naive attempt at porting lower level types for expressions. If you want to have a look (can't add multiple reviewers until it goes public).
Suggestion.
It would be possible to consider the whole expression + symbol as a single enum to preserve the monoid properties of addition and other operations. In the sense that:
(operator, list<expression>)
is an expressionThat would make Int(...) + Int(…) : Expr
the same way Expr(…) + Symbol(…) : Expr
use num::Complex;
#[derive(Debug, PartialEq)]
pub enum Operator {
Add,
Mul,
Pow,
NonComm,
Call
}
#[derive(Debug, PartialEq)]
pub enum Numerical {
Int(i64),
Float(f64),
Complex(Complex<f64>),
}
#[derive(Debug, PartialEq)]
pub enum Expression {
Symbol(&'static str),
Value(Numerical),
Expr { head: Operator, args: Vec<Box<Expression>> },
}
@kaosmicadei Makes sense. Lemme have a go at it.
Suggestion.
It would be possible to consider the whole expression + symbol as a single enum to preserve the monoid properties of addition and other operations. In the sense that:
Naive question: would anything else than an enum break monoidal properties ?
Suggestion. It would be possible to consider the whole expression + symbol as a single enum to preserve the monoid properties of addition and other operations. In the sense that:
Naive question: would anything else than an enum break monoidal properties ?
Quick comment @kaosmicadei. Would it make sense to move all boxing constructors to the convenience functions ?
Quick comment @kaosmicadei. Would it make sense to move all boxing constructors to the convenience functions ?
Based on https://doc.rust-lang.org/std/macro.vec.html we could do something like
macro_rules! vbox {
() => { vec![] };
($elem:expr; $n:expr) => { vec![Box::new($elem); $n] };
($($x:expr),+ $(,)?) => { vec![$(Box::new($x)),*] };
}
Actually, in our case, the second pattern could be omitted.
The macro implementation.
macro_rules! vbox {
() => { vec![] };
($($x:expr),+ $(,)?) => { vec![$(Box::new($x)),*] };
}
macro_rules! impl_binary_operator_for_expression {
($trait:ident, $method:ident, $operator:path) => {
impl $trait for Expression {
type Output = Self;
fn $method(self, other: Self) -> Self {
use Expression::*;
match (self, other) {
(Value(x), Value(y)) => Value(x.$method(y)),
(Expr {head: $operator, args: args_lhs}, Expr {head: $operator, args: args_rhs}) => {
let args = args_lhs.into_iter().chain(args_rhs.into_iter()).collect();
Expr{head: $operator, args}
},
(Expr {head: $operator, args: mut args_lhs}, rhs) => {
args_lhs.push(Box::new(rhs));
Expr {head: $operator, args: args_lhs}
},
(lhs, Expr {head: $operator, args: mut args_rhs}) => {
args_rhs.push(Box::new(lhs));
Expr {head: $operator, args: args_rhs}
},
(lhs, rhs) => Expr{head: $operator, args: vbox![lhs, rhs]},
}
}
}
};
($trait:ident, $method:ident, $operator:path, $inv:expr) => {
impl $trait for Expression {
type Output = Self;
fn $method(self, other: Self) -> Self {
use Expression::*;
match (self, other) {
(Value(x), Value(y)) => Value(x.$method(y)),
(lhs, rhs) => Expr {
head: $operator,
args: vbox![lhs, $inv(rhs)]
},
}
}
}
}
}
impl_binop!(Add, add, Operator::ADD);
impl_binop!(Mul, mul, Operator::MUL);
impl_binop!(Sub, sub, Operator:: ADD, |x: Expression| { x.neg() });
impl_binop!(Div, div, Operator:: ADD, |x: Expression| { x.pow(Expression::float(-1.0)) });
It requires the implementation of the Neg
trait.
The macro implementation. ...
It would also require the pow
trait, wouldn't it ?
The macro implementation. ...
It would also require the
pow
trait, wouldn't it ?
This one? I think it could be optional, but maybe by completeness
The macro implementation. ...
It would also require the
pow
trait, wouldn't it ?This one? I think it could be optional, but maybe by completeness
Yes, I was on that one too.
Closes #16