Depends on D20446. Currently, chart functions are both configured through arguments and evaluated through arguments. This sort of conflates things and makes some logic more difficult than it should be.
Instead:
- Function arguments are used to configure function behavior. For example, scale(2) configures a function which does f(x) => 2 * x.
- Evaluation is now separate, after configuration.
We can get rid of "sourceFunction" (which was basically marking one argument as "this is the thing that gets piped in" in a weird magical way) and "canEvaluate()" and "impulse".
Sequences of functions are achieved with compose(u, v, w), which configures a function f(x) => w(v(u(x))) (note order is left-to right, like piping x | u | v | w to produce y).
The new flow is:
- Every chartable function is compose(...) at top level, and composes one or more functions. compose(x) is longhand for id(x). This just gives us a root/anchor node.
- Figure out a domain, through various means.
- Ask the function for a list of good input X values in that domain. This lets function chains which include a "fact" with distinct datapoints tell us that we should evaluate those datapoints.
- Pipe those X values through the function.
- We get Y values out.
- Draw those points.
Also:
- Adds accumluate().
- Adds sum(), which is now easy to implement.
- Adds compose().
- All functions can now always evaluate everywhere, they just return null if they are not defined at a given X.
- Adds repeatable arguments for compose(f, g, ...) and sum(f, g, ...).