📖
A General Introduction to Contextual Programming
  • A General Introduction to Contextual Programming
  • Chapter 1 - Thinking Contextually
    • 1.1 What is a Paradigm?
    • 1.2 What is Contextual Programming?
  • Chapter 2 - Creating Context
    • 2.1 Organizing Data
    • 2.2 Decorators
    • 2.3 Adaptation
  • Chapter 3 - Evaluating with Operations
    • 3.1 Hello World!
    • 3.2 Expanding on 'When'
    • 3.3 Operation Hierarchies
  • Chapter 4 - Reacting with Behaviors
    • 4.1 Revisiting Hello World!
    • 4.2 From 'When' to 'Whenever'
    • 4.3 Working with Buckets
    • 4.4 Expanding Purpose
    • 4.5 Adapting Behaviors
  • Chapter 5 - Abstracting Evaluations
    • 5.1 Compositions
    • 5.2 Operables
  • Chapter 6 - Abstracting Contexts
    • 6.1 Contracts
    • 6.2 Context Identifiers
  • Chapter 7 - Looking to What's Next
    • 7.1 Final Thoughts
    • 7.2 Additional Resources
Powered by GitBook
On this page
  • Operation Conditions
  • Operations with Multiple Contexts
  • Qualification Values
  • Execution Order
  • Conditional Evaluations
  1. Chapter 3 - Evaluating with Operations

3.2 Expanding on 'When'

Previous3.1 Hello World!Next3.3 Operation Hierarchies

Last updated 7 months ago

Operation Conditions

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:

Input Number :: an Int? provided by the user : context.

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:

Even Checker :: 
    outputs whether an input number is even : operation 
    when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate response is Console Response,
        evaluate response to Input 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:

Notify Of An Even Number :: 
    outputs when an Input Number is even : operation <number>
    when number % 2 = 0?
        evaluate "That number is even!" as Console Message.

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:

Notify Of An Odd Number :: 
    outputs when an Input Number is odd : operation <number> 
    when number % 2 = 1?
        evaluate "That number is not even!" as Console Message.

Operations with Multiple Contexts

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.

Even Checker :: 
    outputs whether an input number is even : operation 
    when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate response is Console Response;
        evaluate <response, response to Input Number>.

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:

Initiate Valid Input Evaluation :: 
    initiates handling of a valid Input Number from a Console Response :: operation 
    <input, response> when not input (is NaN)?
        evaluate 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:

Notify User Of Bad Input :: 
    outputs a message when a Console Response is not a valid Input Number : operation
    <response, input> when input (is NaN)?
        evaluate "'\(response)' is not a number." as Console Message.

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.

Qualification Values

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:

Input Number :: an Int provided by the user : context.

Then the earlier Initiate Valid Input Evaluation operation can be updated to perform the casting:

Initiate Valid Input Evaluation :: 
    initiates the handling of a valid Console Response :: operation <response>
    where number is response to Input Number?
        evaluate number.

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:

Even Checker :: 
    outputs whether an input number is even : operation when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate Console Response.

The last line here has a syntax different than shown with an evaluate so far. evaluate Console Response specifies that an instance of Console Response will be created, and evaluated, but its value isn't needed for the remainder of this operation, so there is no name specified, and no assignment with is for an explicit variable declaration. This is called an "anonymous evaluation".

However, the new operation, as is, might not always perform as desired, for reasons explained in the next section.

Execution Order

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.

Initiate Valid Input Evaluation :: 
    initiates the handling of a valid Console Response :: operation <response>
    after Capture Console Input As Response,
    where number is response to Input Number?
        evaluate number.

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:

Initiate Valid Input Evaluation :: 
    initiates the handling of a valid Console Response :: operation <response>
    where number is response to Input Number, 
    after all?
        evaluate number.

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).

This example for execution order is a bit more explicit than necessary. In reality, Rede's Capture Console Input As Response already specifies a before all control flow, so in standard use (as in this example), a developer wouldn't need to explicitly state that their own operation to handle a Console Response should be performed after.

Conditional Evaluations

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:

Input Number :: an Int provided by the user : context.

Even Checker :: 
    outputs whether an input number is even : operation when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate Console Response.
    
Initiate Valid Input Evaluation :: 
    initiates the handling of a valid Console Response :: operation <response>
    where number is response to Input Number, 
    after all?
        evaluate number.

Notify Of An Even Number :: 
    outputs when an Input Number is even : operation <number>
    when number % 2 = 0?
        evaluate "That number is even!" as Console Message.

Notify Of An Odd Number :: 
    outputs when an Input Number is odd : operation <number> 
    when number % 2 = 1?
        evaluate "That number is not even!" as Console Message.

The first change is to break the direct connection between the validity check and Console Response. This can be done with an alias:

Potential Number Response :: a potential number from a Console Response: context.

Now the validation operation can use this alias instead of the relational control flow:

Initiate Valid Input Evaluation :: 
    initiates the handling of a Potential Number Response :: operation <response>
    where number is response to Input Number?
        evaluate number.

With an accompanying change to the initial operation:

Even Checker :: 
    outputs whether an input number is even : operation when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate response is Console Response,
        evaluate response as Potential Number Response.

Now comes the work to handle the invalid input. There will need to be one more alias for invalid input:

Invalid Number Response :: 
    a Console Response that is not a number : context.

Then the operation itself:

Notify User Of Bad Input :: 
    outputs a message for an Invalid Number Response : operation <response>?
        evaluate "\(response) is not a number." as Console Message.

Finally, the last update to the initial operation:

Even Checker :: 
    outputs whether an input number is even : operation when initialized?
        evaluate "Please enter a number." as Console Message,
        evaluate response is Console Response,
        evaluate response as Potential Number Response 
            or response as Invalid Number Response.

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 opposite conditional evaluate can be achieved with and, in which the following context(s) will only be evaluated by the evaluate if the preceding evaluation had successfully performed at least one operation.

The use of conditional evaluates allows chaining of evaluations in a logical fashion that furthers the concept of "when" logic occurs.

There are other ways in which this can be accomplished as well! Throughout this book, additional strategies are introduced that could be leveraged to keep the functionality of this code, but to organize the operations and contexts more cleanly.

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:

a later chapter
work around this with an alias
Hello World example
define the context
or the behavior