-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conditional Evaluation #21
Comments
Yeh I agree, having a output type that contains the result of the if in the previous node makes sense. Then when we are doing the codegen we know that out put needs to be put in an if like: let output1 = { // rand true false };
let output2 = { 4 };
if output1 {
println!("{}", output2);
} else {
output2;
let output3 = {5 + output2};
println!("{}", output3);
} |
I'm just having a think about the best approach for this in terms of the I'm finding this a bit tricky to think through in a way that doesn't add a lot more complexity, as even a basic condition has quite a bit more going on than a regular expr. E.g. The current case: let (_output0, _output1, _output2) = { node_expr }; Whereas even for a simple conditional node like:
things get quite a bit more complex, e.g. if cond_expr {
let _output_left: () = expr_left;
} else {
let _output_right: () = expr_right;
} Here it looks like we need three expressions from the node now,
|
Maybe rather than thinking in terms of conditions it would be easier to come up with a more general solution thinking in terms of pattern matching, as pretty much all conditions and pattern matching can be represented with a match pattern_expr {
true => {
let _output_left: () = expr_left;
}
false => {
let _output_right: () = expr_right;
}
} This way we could also allow nodes to specifying the pattern associated with each branch. E.g. above |
Maybe the return type of pub enum Expr {
/// An unconditional expression, e.g. `l + r`.
Unconditional(syn::Expr),
/// An expression that branches based on pattern matching, e.g.
///
/// match a == b {
/// true => { foo(); }
/// false => { bar(); }
/// }
PatternMatch(PatternMatch),
}
pub struct PatternMatch {
/// Expression that will be pattern matched on.
pattern_expr: syn::Expr,
/// The possible branches of evaluation.
branches: Vec<Branch>,
}
pub struct Branch {
/// Pattern that will trigger evaluation of this branch.
pattern: syn::Pat,
/// The node's expression to evaluate for this branch.
expr: syn::Expr,
/// The outputs that will be evaluated by the expr for this branch.
outputs: Vec<Output>,
} Or something along these lines? This way the node can have any number of branches and still evaluate to one or more outputs per branch. Maybe this can be simplified further... |
So then this will be expanded into an actual as in: match p.pattern_expr {
Branch1 => { // some downstream nodes },
Branch2 => { // some other downstream nodes },
} |
By checking the You're right though, this isn't as easy as I was first thinking - we'll need to do another topographic traversal to determine what we can reach from those outputs. Alternatively, we could consider each node's expr when creating the initial evaluation steps and rather than returning a |
I like the idea of returning the tree structure vs the Vec. I'm having a little hard conceptualizing it though. Might make some more drawings. |
Actually what we could do is something like: match p.pattern_expr {
Branch1 => { eval_stream1() },
Branch2 => { eval_stream2() },
} So basically each branch generates another evaluation function. |
Nice, I like this idea! This would add a little more performance overhead though, as we don't know the types of the outputs, we'd have to cast them to This would be really nice though as it would make the generated code a little more modular which might require less work from the compiler each time a branch from the graph is updated. This issue of not having types available is a tricky tradeoff. I might open an issue for it. |
What if each output always produced a result, but it was wrapped in a type like an |
@nebkor this sounds interesting, but I'm not sure I fully understand what you mean. Are you suggesting that each node always returns an |
Always return something like an Option; For richer metadata, instead of an actual enum NodeOutput<T> {
Fresh(T),
Cached(T),
Unevaluated,
} or drop the |
This allows for optionally specifying the full types of the inputs and outputs of a `Node` during implementation by allowing to specify a full, freestanding function, rather than only an expression. The function's arguments and return type will be parsed to produce the number of inputs and outputs for the node, where the number of arguments is the number of inputs, and the number of tuple arguments in the output is the number of outputs (1 if no tuple output type). Some of this may have to be re-written when addressing a few follow-up issues including nannou-org#29, nannou-org#19, nannou-org#21 and nannou-org#22, but I think it's helpful to break up progress into achievable steps! Closes nannou-org#27 and makes nannou-org#20 much more feasible.
This allows for optionally specifying the full types of the inputs and outputs of a `Node` during implementation by allowing to specify a full, freestanding function, rather than only an expression. The function's arguments and return type will be parsed to produce the number of inputs and outputs for the node, where the number of arguments is the number of inputs, and the number of tuple arguments in the output is the number of outputs (1 if no tuple output type). Some of this may have to be re-written when addressing a few follow-up issues including nannou-org#29, nannou-org#19, nannou-org#21 and nannou-org#22, but I think it's helpful to break up progress into achievable steps! Closes nannou-org#27 and makes nannou-org#20 much more feasible.
Currently gantz expects that a node's expression will always evaluate to one instance of each of its outputs. This is a bit limiting as it means we don't have any way to optionally evaluate branches of a graph based on some value at runtime.
To support conditional evaluation I can think of a couple of approaches:
bool
as an input and two conditionally evaluated outputsI'm leaning towards option 1 as it will likely be common to want to create nodes that conditionally evaluate certain outputs. It also simplifies the idea of allowing for graph nodes themselves to have conditionally evaluated outlets.
Conditional Node Outputs
WIP
The text was updated successfully, but these errors were encountered: