Flow.apex is a library to help you weave functions in a procedural style.
Flow.apex is created to simplify the creation of a Function. It simulates the procedural invocation of Functions, and weave them to build a larger and more complicated Function. Flow.apex acts as a bridge between the small Funcs and big custom Funcs that you create by subclassing Func.
Flow.apex has a dependency over R.apex and Script.apex.
Please include them before including Flow.apex.
Flow.apex is in fact an extension of R.apex, and it requires a fair amount of knowledge on R.apex. If you want to go deeper with Flow.apex, please do check out R.apex.
Flow.apex has a built-in FlowScript to run Funcs dynamically.
Previously, we run R.product like this:
Object result = R.product.runN(new List<Object>{ 1, 2, 3, 4});
// Generate 24 from 1 * 2 * 3 * 4Now with FlowScript, we have:
Object result = Flow.eval('product(1, 2, 3, 4)');
// Generate 24 from 1 * 2 * 3 * 4FlowScript makes invocations of Funcs more natural.
A Flow is actually a Func.
Func f = new Flow()
.inputAs('a', 'b').returnInteger();Here we create a Func of (a, b) => Integer, with an empty body.
Or we can give it a name.
Flow f = new Flow('f')
.input('a', 'b').returnInteger();This will add Func f to FlowScript, so that it can be referenced in the future.
This definition creates a Func that takes a and b as arguments, and returns Integer.
As a Flow is jsut a Func, we can use it wherever we can use Funcs.
Object result = f.run(1, 2); // Call the Flow(Func)We can do variable assignment in Flows.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('num = 0');Here we created a local variable num with the value of 0.
Flows have global variables and local variables. Any variable that is passed from outside is a global one. Any variable that is created inside the Flow is a local one. Variables cannot share the same names, so there is no variable overridden in Flow.apex.
We can invoke functions in Flows.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('num = add(a, b)');Here we assign the value of num to be the result of calling add(a, b).
Funcs from R.apex are imported in Flow.apex. If you want to import custom Funcs, see below.
Flow.addFunc('plus', R.add);This will register the Func R.add in the name of plus. So you can refer it now in FlowScript.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('num = plus(a, b)');returnXxx in Flow.apex only specifies what type of result is returned. To actually return something, call doReturn(Object) or doReturnRaw(Object).
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doReturn(0);If no doReturn or equivalent is added, the Flow Func will return null.
The difference between doReturn and doReturnRaw is that doReturn treats String as FlowScript while doReturnRaw does not.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doReturn('"message"');is equivalent to
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doReturnRaw('message');Flow.apex supports if and if not blocks.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doIf(
'a == 1',
Flow.block().doReturn(1)
);And we can append the else block too.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doIf(
'a == 1',
Flow.block().doReturn(1),
Flow.block().doReturn(2)
);Here if needs at least one block. A block in Flow.apex is actually a block of executable statements. We can use the blocks to extend our logic.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doIf(
'a == 1',
Flow.block()
.var('b = 2')
// more logic goes on here
.doReturn(1)
);We support two types of for blocks in Flow.apex.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doFor('i = 0; i < 10; i = i + 1', Flow.block()
.var('output = debug(i)')
);Or
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.doFor('i in range(0, 10)', Flow.block()
.var('output = debug(i)')
);We can use while blocks in Flow.apex like this.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('i = 0')
.doWhile('i < 10', Flow.block()
.var('output = debug(i)')
.var('i = i + 1')
);We can use break and continue in any loop blocks.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('i = 0')
.doWhile('i < 10', Flow.block()
.doIf('i == 3', Flow.block()
.doBreak()
)
.var('output = debug(i)')
.var('i = i + 1')
);We can use switch in Flows too.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.var('word = "a"')
.doSwitch('word', new List<Object>{
'a', Flow.block().var('output = debug("Matched")'),
'b', Flow.block().var('output = debug("Not Matched")')
});Also we can use break in switch blocks.
With Flow.apex, recursion is not difficult to achieve.
Flow f = new Flow()
.inputAs('n').returnInteger()
.doIf(
'n == 0',
Flow.block()
.doReturn(0)
)
.var('ret = 2 + this(n - 1)')
.doReturn('ret');We can add debugging information anywhere we want in the block.
Flow f = new Flow()
.inputAs('a', 'b').returnInteger()
.debug();debug will print all of the current variables in the block.