5.2 Operables
Assembling Contexts
As briefly noted before, 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 compositions, operables can be used as declared variables within operations, and they can be provided as inputs to an operation. The FizzBuzz example from earlier can provide some familiar material to see how this can be done. Here is one version of that completed example:
Fizz Buzz Input String :: a Console Response used to perform fizz buzz : context.
Fizz Buzz :: an Int representing the fizz buzz number : context.
Run Fizz Buzz :: operation when initialized?
evaluate "Please enter a number." as Console Message,
evaluate Console Response as Fizz Buzz Input String.
Evaluate User Input :: evaluates the user's provided Fizz Buzz Input String :
operation <input>.
Initiate Fizz Buzz [Evaluate User Input] :: operation
fizz buzz is input as Fizz Buzz,
when fizz buzz > 0?
evaluate fizz buzz.
Notify of Invalid Input [Evaluate User Input] :: operation
default?
evaluate "That input is not valid for Fizz Buzz!" as Console Message.
Perform Fizz Buzz :: performs Fizz Buzz and outputs results : operation <fizz buzz>
foreach i in Range[1, fizz buzz].
Divisible by 3 [Perform Fizz Buzz] :: operation
when i % 3
without Divisible By 15?
evaluate "Fizz" as Console Message.
Divisible by 5 [Perform Fizz Buzz] :: operation
when i % 5
without Divisible By 15?
evaluate "Buzz" as Console Message.
Divisible by 15 [Perform Fizz Buzz] :: operation
when i % 15?
evaluate "Fizz Buzz" as Console Message.
Otherwise [Perform Fizz Buzz] :: operation
default?
evaluate i to Console Message.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.
Also similar to compositions, 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.
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.
Operable Types
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 required 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.
Last updated