### Flowing [ Funcon left-to-right Alias l-to-r Funcon right-to-left Alias r-to-l Funcon sequential Alias seq Funcon effect Funcon choice Funcon if-true-else Alias if-else Funcon while-true Alias while Funcon do-while-true Alias do-while Funcon interleave Datatype yielding Funcon signal Funcon yielded Funcon yield Funcon yield-on-value Funcon yield-on-abrupt Funcon atomic ] Meta-variables T <: values T* <: values* #### Sequencing Funcon left-to-right(_:(=>(T)*)*) : =>(T)* Alias l-to-r = left-to-right /* `left-to-right(...)` computes its arguments sequentially, from left to right, and gives the resulting sequence of values, provided all terminate normally. For example, `integer-add(X, Y)` may interleave the computations of `X` and `Y`, whereas `integer-add left-to-right(X, Y)` always computes `X` before `Y`. When each argument of `left-to-right(...)` computes a single value, the type of the result is the same as that of the argument sequence. For instance, when `X:T` and `Y:T'`, the result of `left-to-right(X, Y)` is of type `(T, T')`. The only effect of wrapping an argument sequence in `left-to-right(...)` is to ensure that when the arguments are to be evaluated, it is done in the specified order. */ Rule Y ---> Y' ------------------------------------------------------------ left-to-right(V*:(T)*, Y, Z*) ---> left-to-right(V*, Y', Z*) Rule left-to-right(V*:(T)*) ~> V* Funcon right-to-left(_:(=>(T)*)*) : =>(T)* Alias r-to-l = right-to-left /* `right-to-left(...)` computes its arguments sequentially, from right to left, and gives the resulting sequence of values, provided all terminate normally. Note that `right-to-left(X*)` and `reverse left-to-right reverse(X*)` are not equivalent: `reverse(X*)` interleaves the evaluation of `X*`. */ Rule Y ---> Y' ------------------------------------------------------------ right-to-left(X*, Y, V*:(T)*) ---> right-to-left(X*, Y', V*) Rule right-to-left(V*:(T)*) ~> V* Funcon sequential(_:(=>null-type)*, _:=>T) : =>T Alias seq = sequential /* `sequential(X, ...)` computes its arguments in the given order. On normal termination, it returns the value of the last argument; the other arguments all compute `null-value`. Binary `sequential(X, Y)` is associative, with unit `null-value`. */ Rule X ---> X' ----------------------------------------- sequential(X, Y+) ---> sequential(X', Y+) Rule sequential(null-value, Y+) ~> sequential(Y+) Rule sequential(Y) ~> Y Funcon effect(V*:T*) : =>null-type ~> null-value /* `effect(...)` interleaves the computations of its arguments, then discards all the computed values. */ #### Choosing Funcon choice(_:(=>T)+) : =>T /* `choice(Y, ...)` selects one of its arguments, then computes it. It is associative and commutative. */ Rule choice(X*, Y, Z*) ~> Y Funcon if-true-else(_:booleans, _:=>T, _:=>T) : =>T Alias if-else = if-true-else /* `if-true-else(B, X, Y)` evaluates `B` to a Boolean value, then reduces to `X` or `Y`, depending on the value of `B`. */ Rule if-true-else(true, X, Y) ~> X Rule if-true-else(false, X, Y) ~> Y #### Iterating Funcon while-true(B:=>booleans, X:=>null-type) : =>null-type ~> if-true-else(B, sequential(X, while-true(B, X)), null-value) Alias while = while-true /* `while-true(B, X)` evaluates `B` to a Boolean value. Depending on the value of `B`, it either executes `X` and iterates, or terminates normally. The effect of abruptly breaking the iteration is obtained by the combination `handle-break(while-true(B, X))`, and that of abruptly continuing the iteration by `while-true(B, handle-continue(X))`. */ Funcon do-while-true(X:=>null-type, B:=>booleans) : =>null-type ~> sequential(X, if-true-else(B, do-while-true(X, B), null-value)) Alias do-while = do-while-true /* `do-while-true(X, B)` is equivalent to `sequential(X, while-true(B, X))`. */ #### Interleaving Funcon interleave(_:T*) : =>T* /* `interleave(...)` computes its arguments in any order, possibly interleaved, and returns the resulting sequence of values, provided all terminate normally. Fairness of interleaving is not required, so pure left-to-right computation is allowed. `atomic(X)` prevents interleaving in `X`, except after transitions that emit a `yielded(signal)`. */ Rule interleave(V*:T*) ~> V* Datatype yielding ::= signal Entity _ --yielded(_:yielding?)-> _ /* `yielded(signal)` in a label on a transition allows interleaving at that point in the enclosing atomic computation. `yielded( )` indicates interleaving at that point in an atomic computation is not allowed. */ Funcon yield : =>null-type ~> yield-on-value(null-value) Funcon yield-on-value(_:T) : =>T /* `yield-on-value(X)` allows interleaving in an enclosing atomic computation on normal termination of `X`. */ Rule yield-on-value(V:T) --yielded(signal)-> V Funcon yield-on-abrupt(_:=>T) : =>T /* `yield-on-abrupt(X)` ensures that abrupt termination of `X` is propagated through an enclosing atomic computation. */ Rule X --abrupt(V:T),yielded(_?)-> X' -------------------------------------------------------------------- yield-on-abrupt(X) --abrupt(V),yielded(signal)-> yield-on-abrupt(X') Rule X --abrupt( )-> X' ---------------------------------------------------- yield-on-abrupt(X) --abrupt( )-> yield-on-abrupt(X') Rule yield-on-abrupt(V:T) ~> V Funcon atomic(_:=>T) : =>T /* `atomic(X)` computes `X`, but controls its potential interleaving with other computations: interleaving is only allowed following a transition of `X` that emits `yielded(signal)`. */ Rule X --yielded( )->1 X' atomic(X') --yielded( )->2 X'' ----------------------------------------------- atomic(X) --yielded( )->1 ; --yielded( )->2 X'' Rule X --yielded( )-> V V : T --------------------------- atomic(X) --yielded( )-> V Rule atomic(V:T) ~> V Rule X --yielded(signal)-> X' ----------------------------------- atomic(X) --yielded( )-> atomic(X')