5.2 Operables
Last updated
Last updated
As briefly noted , contexts can be grouped together to form an operable. Operables are what evaluate
actually evaluates to initiate operations. Just like a single context is implicitly cast to a composition for activate
and deactivate
, a single context is implicitly cast to an operable for evaluate
. The grouping of contexts with < >
prior to an evaluate
is an explicit in-line declaration of an operable for those contexts.
Just as with , operables can be used as declared variables within operations, and they can be provided as inputs to an operation. The from earlier can provide some familiar material to see how this can be done. Here is one version of that completed example:
The Perform Fizz Buzz
operation, and more specifically its child operations, results in some string being output to the console for every count up to the input number. This is completely fine for a straightforward Fizz Buzz implementation, but it can be altered to make the result more dynamic by abstracting the console messages away from Perform Fizz Buzz
and its children. The first step to do so is to declare operables, in Initiate Fizz Buzz
, that will later take the place of any evaluation of a Console Message
:
The new lines, like on fizz buzz is <"Fizz Buzz" as Console Message>
, are declaring operable variables. Any of which can be evaluated to produce the same as evaluate <"Fizz Buzz" as Console Message>
, which, for a single context, is the same as evaluate "Fizz Buzz" as Console Message
. The line to start the Fizz Buzz algorithm has also been changed, now being evaluate <fizz buzz, on fizz buzz, on buzz, on fizz, on default>
, where there is an in-line operable declaration to group the original Fizz Buzz
context with the new operables.
Perform Fizz Buzz
will need to update as well, as right now it wouldn't ever be performed with the new Initiate Fizz Buzz
code:
It will be performed with these changes, but it isn't really using the operables. That can be changed by updating all of its evaluate
calls to use the operables instead of its own Console Message
contexts:
The default evaluate
is different than might have been expected, now being evaluate on default + i as Neither Fizz Nor Buzz Number
(a new context declared at the top). This isn't appending i
to the operable's Console Message
. This is appending i
, as a Neither Fizz Nor Buzz Number
context to the operable. For this to evaluate to something, there will need to be a new operation that expects a Console Message
and a Neither Fizz Nor Buzz Number
, like so:
The Fizz Buzz example works again with Perform Fizz Buzz
and its children no longer concerned about the specific messages that should be output for each case of the algorithm. This abstraction can go a step further though. As currently implemented, Perform Fizz Buzz
doesn't really care about what contexts the input operables have, so they could be reduced to the basic Operable
type:
Now the input operables can really be anything. Perform Fizz Buzz
and its children aren't even aware of whether messages are being output anymore, they are purely focused on performing the algorithm up to the input number. However, they are still assuming there will be an operation that can handle the input on default
operable with a Neither Fizz Nor Buzz Number
context. That can be addressed in many ways, one of which is covered below.
As with any other type that can be used as a variable, a more specific type can be declared. This would permit abstracting away from <Console Message>
as an expected operable type to a different type that can be changed over time, without Perform Fizz Buzz
needing to update for it. This imparts the same benefit as using only the basic Operable
type, but while giving Perform Fizz Buzz
some control over the basics of the operables that it will accept. The first step to do so is to declare those operable types:
Now, the operable instance declarations can be updated:
Notice how On Default
is defined as an operable for Neither Fizz Nor Buzz Number
, but when being created it is provided an Int, to be cast to the Neither Fizz Nor Buzz Number
and a Console Message
. Operable types, like compositions, are fulfilled when they have at least the stated contexts of the type, but they can include additional contexts as well, which influence the final evaluation. In this case, on default
is an operable that meets the expectations of On Default
with an additional Console Message
context, which will enable its evaluation to meet the requirements for the Output Non Fizz Buzz
operation.
The Perform Fizz Buzz
declaration needs updating as well:
The resulting code maintains most of its abstraction, is cleaner looking, and more readable. Perform Fizz Buzz
requires its specific operables, but all of them except On Default
could change freely. Only On Default
is to maintain Neither Fizz Nor Buzz Number
as its first context for Otherwise
to ultimately update with its own number. As discussed above, the provided on default
operable can include any number of other contexts that ultimately determine what operation(s) will be performed upon evaluate on default
.
While not explicitly discussed, compositions used as inputs to operations work the same way as operables. There is the basic Composition
type for the loosest abstraction and the more restrictive custom types that allow the operation to inject its own contexts, with any number of other contexts possibly being carried along with the input composition to influence the eventual evaluation.
Also similar to , the elements of an operable are still operables. So even though an operation may have one of these declared operables input, it can't get the contexts within it out to reference or change directly. It can alter the operable itself though, such as by changing the contexts for different contexts.