4.3 Working with Buckets
Last updated
Last updated
Some behaviors will need to operate for every instance of a type of context. The created in the last chapter is an example of such a behavior. Up to this point, its declaration looks like:
As it is currently implemented, there will be an instance of this behavior for every Log Message
(assuming there is only one Log Settings
in the application). That instance will be destroyed after its initialization operation executes. There isn't anything wrong with this implementation, but it does require a bit more work by the application to continually cycle through new instances of the behavior. It also requires the Log Settings
to be maintained by another behavior so it would persist across the destruction of this behavior's instances.
These requirements can be removed by making Perform Logging
dependent on the application's collection of Log Message
instances rather than a single instance. Doing so can be done by updating the behavior, as:
By making the behavior dependent on a collection, an instance of Log Settings
on its own will be enough to qualify an instance of the behavior, albeit with an empty Log Message
bucket, which is fine for this behavior. Whenever the number of messages in the Log Message
bucket is more than 0
, as specified by whenever
> 0
, the Evaluate Messages
operation will reactively evaluate, being executed for every message
that is now in the bucket.
Within this operation, there is a composition-based evaluation. evaluate message for settings
initiates the evaluation of any operations that depend upon a Log Message
and a composition that consists only of the Log Settings
. This evaluation could extend beyond this behavior, to any other behavior that is working with the same Log Settings
instance and has an operation for a single Log Message
. It would not evaluate any operations that require any Log Message
and any Log Settings
, as that is not what is being specified by this evaluation.
The one operation that currently depends on a Log Message
for that specific Log Settings
instance is the Perform Logging
behavior's Log
operation, as also shown above. It will qualify if the message's log level meets the setting's minimum log level and will then evaluate the message as a Console Message
with Log:
prepended, just as logging was accomplished before these changes. Once that is done, Evaluate Messages
will deactivate the message, regardless of whether it was logged.
These changes accomplish the same functionality as before, but there no longer needs to be a behavior to maintain the Log Settings
between Log Message
instances. The single Perform Logging
behavior instance created for the Log Settings
will persist until Log Settings
is deactivated, which also grants an improvement to performance.
The above changes are an improvement, but they can go a step further to make the behavior as a whole more focused and explicit in its purpose. This can be accomplished by altering what qualifies for the Log Message
bucket.
Operations can use buckets the same as any other input context, and just like behaviors, they can customize the contexts of those buckets into new qualification buckets before using them.
A behavior can ensure a bucket is sorted per a mapping. Mappings are a restricted way to make an immediate change to a value, often converting the value into a new instance of a different type in the process. In the case of sorting, the mapping will evaluate two inputs to either a or an Int
, which will be used to specify whether the first value should be sorted higher than the second value; False
or a negative number meaning the first is less-than the second, Unknown
or 0
meaning that they are equal (there is no measured distinction between the two), and True
or a positive number meaning that the first is greater-than the second.
While not useful for the purpose of this logging implementation, Perform Logging
could be updated to show sorting:
This sorting functionality will occur prior to whenever the behavior will perform a reactive operation, if the messages bucket either has new contexts or has had contexts change their state, thus ensuring that the messages bucket is only sorted right before it is used.
Filtering contexts is the second way behaviors can control the contents of their buckets, and it's the method that will be relevant to further improving the Perform Logging
implementation. Filtering is accomplished in a fashion similar to sorting, where a mapping (the filter logic) is specified after the keywords filtered by
, except this mapping takes only one input and outputs a Bool
to indicate whether the input passes the filter. Using a filter, Perform Logging
can move the qualification of the Log
operation to the behavior's bucket, thus simplifying the overall implementation by condensing its operations:
The first change to point out is {*Log Message*}
replacing Log Message
in the behavior description. This change is what makes the behavior collection dependent. Surrounding a context type with curly braces and stars in Rede, generally as {* "Context Type" *}
, defines what is called a "bucket" of those contexts. A bucket is an collection of contexts. Contexts can be accessed sequentially (as shown above by foreach message in messages
), by index (e.g., messages(0)
), or by . When used in a behavior declaration, by default, all instances of the specified context will be provided to the behavior's bucket. However, that can be customized, as discussed .
There are a couple of ways to customize the contexts of a behavior's bucket(s) as a new (a new bucket in this case). The second of which will be applied to the Perform Logging
behavior.
The above adds a qualification to the behavior, where messages is messages sorted by [(first, second) => first(level) - second(level)]
. This will ensure messages are sorted by their log levels, in order of the lowest log level to the highest. The keywords to define the sort are sorted by
, which will apply the subsequent mapping [(first, second) => first(level) - second(level)]
to the bucket, messages
. The mapping is subtracting the log levels of the two inputs, which can be done since a Log Level
is of an Int
. The result will be an Int
indicating whether the first should be sorted higher or lower than the second. The result of the sort is assigned to a new qualification bucket that is also called messages
, since all mappings and their uses in Rede are performed without .
This makes a couple of assumptions about what will use the Log Message
context and when it should be deactivated. These assumptions will be discussed and corrected with the help of an abstraction concept covered .
Now a Log Message
will only qualify for Perform Logging
, as an element of a new messages
qualification bucket, if its log level is higher than the minimum log level specified in the settings. Any time there is at least one such message Perform Logging
will loop through all the messages, log them as a Console Message
and deactivate them. that the qualification value messages
is replacing the original messages
, so the Perform Logging
behavior will only form a relationship with the remaining contexts of the new bucket. It isn't concerned with any Log Message
that doesn't qualify, and such a context will automatically be deactivated if it doesn't qualify for some other behavior. If a Log Message
were to be persisted by another behavior and should then later qualify for Perform Logging
by changing its level
, then that Log Message
would be added to messages
.