3.2 Expanding on 'When'
Last updated
Last updated
In the, when initialized
was introduced as a Circumstantial Control Flow qualification. However, when
can specify many other qualifications. Most often these qualifications will be conditions that the contexts of an operation must meet for the operation to be performed.
A simple example can be one that checks whether an input number is even. As before, it's best to that we will use first:
This context, Input Number
, is an alias for Int?
, which is a special kind of Int
. An Int?
has the same operations as an Int
, but it always results in another Int?
, since it accounts for the possibility that the value is actually NaN
(Not a Number.) The ability to represent a non-numeric value enables Int?
to more fluidly be used when casting from a String
or dividing by another value that may be 0
.
It's possible to do those things with a normal Int
as well, but only while qualifying for an operation, to enforce a safety standard expected throughout Rede. The usefulness of Int?
will be made apparent later in this section.
Then the entry point to the application, where the user will be asked to provide a number:
Notice how response
is being mapped to an Input Number
using the to
keyword. response
is a Console Response
which is an alias for a String
which, in Rede, has a built-in mapping to an Int?
, which Input Number
is an alias for. The full steps of what is occurring here are: response
being checked for a direct mapping to Input Number
, when this isn't found response
is checked for a direct mapping to Int?
, when this isn't found response
is de-aliased as a String
which is checked for a mapping to Input Number
, when this isn't found response
(as a String
) is checked for a mapping to an Int?
, which is found. The mapping is then performed and the Int?
is aliased back to an Input Number
.
This initial operation requests a number from the user, then waits for the response, and then evaluates any operations that handle the response as an Input Number
. Here is an operation to do just that:
This operation will be performed for an evaluated Input Number
which it will call number
. number
must be even ( 2, leaving no remainder) for the operation to be performed, as specified by when number % 2 = 0
. Upon being performed, the operation sends a console message that the number is even.
It may be nice to also notify the user of when the number is not even, which can be done with this operation:
The initial implementation of Even Checker
doesn't validate whether the user actually provided a number. It wouldn't crash for non-numerical input, a scenario that can't occur in Rede, but some kind of feedback for that scenario would be nice. The implementation can be updated to use aliases that will make use of Rede's built-in validation operations.
The initial operation has a new line evaluate <response, response to Input Number>,
that evaluates the response alongside the response being cast to an Input Number
. Evaluating these two contexts together, as is denoted by the < >
surrounding them, will initiate any operations that require both a Console Response
and an Input Number
.
The order of contexts in an operable does not determine what operations can qualify. In the given example, the same operation will be performed even if the contexts were reversed as evaluate <response to Input Number, response>
. This provides flexibility in how operations define themselves, without binding evaluations to that definition.
In cases where there are multiples of the same context type, such as an operation taking two Console Message
contexts, then the evaluation's contexts will be assigned in the order (for each type of context) that they are specified in the evaluate ...
statement.
As mentioned before, Input Number
is an Int?
, which has an is NaN
property, which is a Bool
(true or false). This property is True
if the String
that was cast to the Int?
was not actually a number. Knowing this, two operations can handle the different states of the user's input:
The above operation will simply propagate the Input Number
to the earlier operations when the input is valid. It will initiate either the Notify Of An Even Number
or Notify Of A Not Even Number
operation. However, it will only be performed if the input was actually a valid number, if it was not, then the following operation can be performed:
Notice how functionality was added only by adding this new operation. In the traditional implementation of most other paradigms, this isn't the case. It is usually not sufficient to add only the new functionality, as the programmer must also change any related functionality that will use it. Contextual Programming decouples functionality from other functionality through the use of evaluate
which indirectly executes the operations that qualify for the specified contexts.
As such Contextual Programming only requires changes beyond the new functionality when there are new contexts to work with or in some cases when contexts are changed.
The validation performed in the last section is one way to ensure the application does not crash when converting invalid input to the expected data type. Another way this can be done is within an operation's qualifications. To show this, the Input Number
context can be redefined to alias an Int
instead of an Int?
, as so:
Then the earlier Initiate Valid Input Evaluation
operation can be updated to perform the casting:
This operation will occur for the same evaluate response is Console Response
that is specified in the initial operation. It will take that user's response and attempt to declare a qualification value, number
, for the operation that maps response
to an Input Number
(which is now aliasing an Int
). The keywords here are where
(to declare a qualification value), is
(to assign to the value), and to
(to attempt the cast from response
to Input Number
). If this fails for any reason, then the operation won't be performed, but the application will not crash, as this failure is treated the same as any other failure for a context's state to qualify for an operation.
There are many scenarios in which qualification values can be declared. Whenever such values are declared, they are preceded by where
, as is shown above. These values are considered inputs as part of the evaluation of the operation, but they cannot affect the state of the actual inputs provided.
With this new operation performing the validation and the mapping, the initial operation can be changed to:
However, the new operation, as is, might not always perform as desired, for reasons explained in the next section.
When more than one operation qualifies for evaluated context(s), the order of those operations being performed might matter, or it might not. Looking back at the last section's Initiate Valid Input Evaluation
operation, it's noteworthy that it takes the same Console Response
context as some other built-in operation. That built-in operation is the operation that polls for the user's input to the console and then captures it within the Console Response
context.
Both of these operations taking the same context is what permits the last section's Even Checker
operation to be so simple, with that evaluate Console Response
line both getting the user's input and initiating the check for that input. However, this only works if those operations' executions occur in the expected order; specifically, Initiate Valid Input Evaluation
must occur after the context is populated with the user's input.
However, there is a statement for Relational Control Flow in Rede that is sufficient in this specific use case. This is the second concept to more generally define order:
In specifying all
after after
, Initiate Valid Input Evaluation
will occur after all other operations for Console Response
have completed. Be careful though, this is not very extendible with more operations, as this operation will now always try to occur last (or at the same time as another operation that wants to be last).
The last two sections offer a more concise version of this implementation than that of the earlier sections, but this implementation lacks the ability to notify the user of invalid input, which is something that was achievable in earlier implementations. Handling invalid input can be accomplished while still keeping much of the latest changes, but there need to be more changes in a few places, so let's review the code as it currently is:
The first change is to break the direct connection between the validity check and Console Response
. This can be done with an alias:
Now the validation operation can use this alias instead of the relational control flow:
With an accompanying change to the initial operation:
Now comes the work to handle the invalid input. There will need to be one more alias for invalid input:
Then the operation itself:
Finally, the last update to the initial operation:
This last update changed evaluate response as Potential Number Response
to evaluate response as Potential Number Response or response as Invalid Number Response
. The noteworthy part of this change is the or
, which defines the context(s) that follow as conditional evaluations to be performed. It basically says "if no operation occurs for response as Potential Number Response
, then evaluate response as Invalid Number Response
.
This line results in response
being aliased as Potential Number Response
which will attempt to perform the Initiate Valid Input Evaluation
operation. If that operation does not qualify, then response
will be aliased as Invalid Number Response
and Notify User of Bad Input
will be performed instead.
The use of conditional evaluates allows chaining of evaluations in a logical fashion that furthers the concept of "when" logic occurs.
The angle brackets < >
around contexts actually builds an instance of a built-in type, in Rede, called an "operable". These can be evaluated to initiate operations that use all of the contexts contained within the operable and are reviewed in more detail in .
The values can even have the same name as their operation's input contexts, but doing so essentially replaces the context that the operation was evaluated with, without changing the external state of the context that was initially evaluated. The use of such a context, from the perspective of the operation () remains the same for evaluation purposes, however it will have lost direct access to that original context, potentially limiting the operation from changing the state of that context since it can only manipulate the newly declared qualification value.
While there are ways to and another evaluate
, Rede offers two concepts that provide a more direct solution. The first is to leverage the names of operations to explicitly designate an order through Relational Control Flow, where operations define their control flow relative to one another. This control flow is defined by keywords, such as after
and before
. Using this control flow, Check Response
can have its order relative to the identified Capture Console Input As Response
(the built-in operation) specified explicitly: